From c3d632274ac0280bb3ba36f610156ec06c6999d3 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 24 May 2025 16:50:50 -0600 Subject: [PATCH 01/77] Ensure we conform to the API specification as closely as possible --- module/Apps/StatsViewer.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/Apps/StatsViewer.mjs b/module/Apps/StatsViewer.mjs index bfa8447..14c61cd 100644 --- a/module/Apps/StatsViewer.mjs +++ b/module/Apps/StatsViewer.mjs @@ -84,11 +84,11 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { // #endregion Instance Data // #region Lifecycle - async render({ userUpdated, ...opts } = {}) { + async render({ userUpdated, ...opts } = {}, _options) { if (userUpdated && !this._selectedUsers.includes(userUpdated)) { return; } - await super.render(opts); + await super.render(opts, _options); }; async _onFirstRender(context, options) { From 354b22da57e4e50c682002eabc78a88ff96adb75 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 25 May 2025 02:00:13 -0600 Subject: [PATCH 02/77] Add data validation and a world setting for enabling the Global API --- module/api.mjs | 60 ++++++++++++++------------ module/hooks/init.mjs | 13 ++++++ module/settings/world.mjs | 16 +++++-- module/utils/databases/Database.mjs | 39 ++++++++++++++--- module/utils/databases/UserFlag.mjs | 31 +++++++++++-- module/utils/databases/model.mjs | 67 +++++++++++++++++++++++------ public/langs/en-ca.json | 10 +++++ 7 files changed, 180 insertions(+), 56 deletions(-) diff --git a/module/api.mjs b/module/api.mjs index 45b7b79..0dbaf9d 100644 --- a/module/api.mjs +++ b/module/api.mjs @@ -10,36 +10,42 @@ import { MemoryDatabase } from "./utils/databases/Memory.mjs"; import { UserFlagDatabase } from "./utils/databases/UserFlag.mjs"; // Utils +import { barGraphSchema, numberBucketSchema, rowSchema, stringBucketSchema, tableSchema } from "./utils/databases/model.mjs"; import { filterPrivateRows, PrivacyMode } from "./utils/privacy.mjs"; import { validateBucketConfig, validateValue } from "./utils/buckets.mjs"; const { deepFreeze } = foundry.utils; -Object.defineProperty( - globalThis, - `stats`, - { - value: deepFreeze({ - Apps: { - TestApp, - StatsViewer, - TableCreator, - TableManager, - }, - utils: { - filterPrivateRows, - validateValue, - validateBucketConfig, - }, - enums: { - PrivacyMode, - }, - databases: { - Database, - MemoryDatabase, - UserFlagDatabase, - }, - }), - writable: false, +export const api = deepFreeze({ + Apps: { + TestApp, + StatsViewer, + TableCreator, + TableManager, }, -); + utils: { + filterPrivateRows, + validateValue, + validateBucketConfig, + }, + enums: { + PrivacyMode, + }, + databases: { + Database, + MemoryDatabase, + UserFlagDatabase, + }, + schemas: { + buckets: { + range: numberBucketSchema, + number: numberBucketSchema, + string: stringBucketSchema, + }, + graphs: { + bar: barGraphSchema, + }, + table: tableSchema, + row: rowSchema, + }, +}); diff --git a/module/hooks/init.mjs b/module/hooks/init.mjs index 0c8fcfe..1422bce 100644 --- a/module/hooks/init.mjs +++ b/module/hooks/init.mjs @@ -9,6 +9,7 @@ import { TableCreator } from "../Apps/TableCreator.mjs"; import { TableManager } from "../Apps/TableManager.mjs"; // Misc Imports +import { api } from "../api.mjs"; import helpers from "../handlebarsHelpers/_index.mjs"; import { Logger } from "../utils/Logger.mjs"; import { registerCustomComponents } from "../Apps/elements/_index.mjs"; @@ -45,6 +46,18 @@ Hooks.on(`init`, () => { CONFIG.stats.db = MemoryDatabase; }; + game.modules.get(__ID__).api = api; + if (game.settings.get(__ID__, `globalAPI`)) { + Object.defineProperty( + globalThis, + `stats`, + { + value: api, + writable: false, + }, + ); + }; + Handlebars.registerHelper(helpers); registerCustomComponents(); }); diff --git a/module/settings/world.mjs b/module/settings/world.mjs index 6da838c..7d44034 100644 --- a/module/settings/world.mjs +++ b/module/settings/world.mjs @@ -1,18 +1,26 @@ /* World Settings: - - Track rolls automatically - Track inactive rolls (e.g. the "lower" in a "kh" roll) - - Track self rolls (defaulta false) */ export function registerWorldSettings() { game.settings.register(__ID__, `autoTrackRolls`, { - name: `Roll Auto-Tracking`, - hint: `Whether or not the module should automatically add rolls made in the chat to the database. This is useful if the system you're using has implemented an integration with the module, or if you only want macros to handle the database additions.`, + name: `STAT_TRACKER.settings.autoTrackRolls.name`, + hint: `STAT_TRACKER.settings.autoTrackRolls.hint`, scope: `world`, type: Boolean, config: true, default: true, requiresReload: true, }); + + game.settings.register(__ID__, `globalAPI`, { + name: `STAT_TRACKER.settings.globalAPI.name`, + hint: `STAT_TRACKER.settings.globalAPI.hint`, + scope: `world`, + type: Boolean, + config: true, + default: import.meta.env.DEV, + requiresReload: true, + }); }; diff --git a/module/utils/databases/Database.mjs b/module/utils/databases/Database.mjs index 932ac46..8185b8f 100644 --- a/module/utils/databases/Database.mjs +++ b/module/utils/databases/Database.mjs @@ -1,6 +1,8 @@ /* eslint-disable no-unused-vars */ +import { BucketTypes, validateBucketConfig } from "../buckets.mjs"; +import { Logger } from "../Logger.mjs"; import { PrivacyMode } from "../privacy.mjs"; -import { validateBucketConfig } from "../buckets.mjs"; +import { tableSchema } from "./model.mjs"; /* NOTE: @@ -28,6 +30,12 @@ Default Subtables: const { deleteProperty, diffObject, expandObject, mergeObject } = foundry.utils; +/** + * The generic Database implementation, any subclasses should implement all of + * the required methods, optionally overriding the methods provided by this class, + * data validation should be used on any and all of the create* methods to ensure + * consistency across databases. + */ export class Database { // MARK: Table Ops static async createTable(tableConfig) { @@ -36,25 +44,42 @@ export class Database { return false; }; - const name = tableConfig.name; - if (name.split(`/`).length > 2) { - ui.notifications.error(`Subtables are not able to have subtables`); + const { error, value: corrected } = tableSchema.validate( + tableConfig, + { abortEarly: false, convert: true, dateFormat: `iso`, render: false }, + ); + if (error) { + ui.notifications.error(`Table being created did not conform to required schema, see console for more information.`, { console: false }); + Logger.error(error); return false; }; - const tables = game.settings.get(__ID__, `tables`); + const name = tableConfig.name; const [ table, subtable ] = name.split(`/`); + + const tables = game.settings.get(__ID__, `tables`); if (subtable && tables[table]) { ui.notifications.error(`Cannot add subtable for a table that already exists`); return false; }; + if (table === `Dice`) { + if (!subtable.match(/^d[0-9]+$/)) { + ui.notifications.error(`Cannot create a Dice subtable that doesn't use "dX" as it's subtable name.`); + return false; + }; + if (tableConfig.buckets.type === BucketTypes.RANGE) { + ui.notifications.error(`Cannot create a Dice subtable with a non-range bucket type`); + return false; + }; + }; + if (tables[name]) { ui.notifications.error(`Cannot create table that already exists`); return false; }; - tables[name] = tableConfig; + tables[name] = corrected; game.settings.set(__ID__, `tables`, tables); this.render({ tags: [`table`] }); return true; @@ -99,7 +124,7 @@ export class Database { try { updated.buckets = validateBucketConfig(updated.buckets); } catch (e) { - ui.notifications.error(e); + Logger.error(e); return false; }; diff --git a/module/utils/databases/UserFlag.mjs b/module/utils/databases/UserFlag.mjs index 245a978..7072c5c 100644 --- a/module/utils/databases/UserFlag.mjs +++ b/module/utils/databases/UserFlag.mjs @@ -1,6 +1,7 @@ import { filterPrivateRows, PrivacyMode } from "../privacy.mjs"; import { Database } from "./Database.mjs"; import { Logger } from "../Logger.mjs"; +import { rowSchema } from "./model.mjs"; const { hasProperty, mergeObject, randomID } = foundry.utils; @@ -13,12 +14,22 @@ export class UserFlagDatabase extends Database { let user = game.users.get(userID); if (!table || !user) { return }; - row._id ||= randomID(); + row._id = randomID(); row.timestamp = new Date().toISOString(); + const { error, value: corrected } = rowSchema.validate( + row, + { abortEarly: false, convert: true, dateFormat: `iso`, render: false }, + ); + if (error) { + ui.notifications.error(`Row being created did not conform to required schema, see console for more information.`, { console: false }); + Logger.error(error); + return false; + }; + const userData = user.getFlag(__ID__, dataFlag); userData[tableID] ??= []; - userData[tableID].push(row); + userData[tableID].push(corrected); await user.setFlag(__ID__, dataFlag, userData); if (rerender) { @@ -36,9 +47,21 @@ export class UserFlagDatabase extends Database { userData[tableID] ??= []; for (const row of rows) { - row._id ||= randomID(); + row._id = randomID(); row.timestamp = new Date().toISOString(); - userData[tableID].push(row); + + const { error, value: corrected } = rowSchema.validate( + row, + { abortEarly: false, convert: true, dateFormat: `iso`, render: false }, + ); + if (error) { + ui.notifications.error(`A row being created did not conform to required schema, see console for more information.`, { console: false }); + Logger.error(`Failing row:`, row); + Logger.error(error); + continue; + }; + + userData[tableID].push(corrected); }; await user.setFlag(__ID__, dataFlag, userData); diff --git a/module/utils/databases/model.mjs b/module/utils/databases/model.mjs index caa7b02..ed4afa8 100644 --- a/module/utils/databases/model.mjs +++ b/module/utils/databases/model.mjs @@ -1,7 +1,8 @@ import * as Joi from "joi"; +import { PrivacyMode } from "../privacy.mjs"; // MARK: Buckets -const numberBucketSchema = Joi.object({ +export const numberBucketSchema = Joi.object({ type: Joi.string().valid(`number`, `range`).required(), min: Joi .number() @@ -29,15 +30,18 @@ const numberBucketSchema = Joi.object({ }), }); -const stringBucketSchema = Joi.object({ +export const stringBucketSchema = Joi.object({ type: Joi.string().valid(`string`).required(), - choices: Joi.array(Joi.string()).optional(), + choices: Joi.array().items(Joi.string().trim().invalid(``)).optional(), }); // MARK: Graphs -const barGraphSchema = Joi.object({ +export const barGraphSchema = Joi.object({ type: Joi.string().valid(`bar`).required(), - stacked: Joi.boolean().required(), + stacked: Joi + .boolean() + .default(true) + .optional(), }); // MARK: Table @@ -45,16 +49,51 @@ export const tableSchema = Joi.object({ name: Joi .string() .trim() + .invalid(``) .required() - .pattern(/^[a-z \-_]+(\/[a-z \-_]+)?$/i), - buckets: Joi.alternatives([ - numberBucketSchema, - stringBucketSchema, - ]).match(`one`), - graph: Joi.alternatives([ - barGraphSchema, - ]).match(`one`), + .pattern(/^[0-9a-z \-_]+(\/[0-9a-z \-_]+)?$/i), + buckets: Joi + .alternatives() + .try( + numberBucketSchema, + stringBucketSchema, + ) + .match(`one`) + .required(), + graph: Joi + .alternatives() + .try( + barGraphSchema, + ) + .match(`one`) + .required(), }); // MARK: Row -export const rowSchema = Joi.object({}); +/** + * The schema for the row objects, this does not validate that the + * value of the row conforms to the bucket configurations, however + * it does validate that the value is at least one of the accepted + * types. For validation of the value itself check "validateValue" + * in `utils/buckets.mjs` + */ +export const rowSchema = Joi.object({ + _id: Joi + .string() + .alphanum() + .required(), + timestamp: Joi + .string() + .isoDate() + .required(), + value: Joi + .alternatives([ + Joi.string().trim().invalid(``), + Joi.number(), + ]) + .required(), + privacy: Joi + .string() + .valid(...Object.values(PrivacyMode)) + .required(), +}); diff --git a/public/langs/en-ca.json b/public/langs/en-ca.json index 4cf777c..7b30eea 100644 --- a/public/langs/en-ca.json +++ b/public/langs/en-ca.json @@ -5,6 +5,16 @@ "Subtable": "Subtable", "Users": "Users", "DataVisibility": "Data Visibility" + }, + "settings": { + "autoTrackRolls": { + "name": "Roll Auto-Tracking", + "hint": "Whether or not the module should automatically add rolls made in the chat to the database. This is useful if the system you're using has implemented an integration with the module, or if you only want macros to handle the database additions." + }, + "globalAPI": { + "name": "Global API", + "hint": "Whether or not the module provides a global interface for interacting with the module's backend. This is convenient for macros and using the dev console." + } } } } From 52a64ca54bde13719425cdc39db0aa6edcea6468 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 25 May 2025 02:01:39 -0600 Subject: [PATCH 03/77] Make the schema a bit easier to read --- module/utils/databases/model.mjs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/module/utils/databases/model.mjs b/module/utils/databases/model.mjs index ed4afa8..0461055 100644 --- a/module/utils/databases/model.mjs +++ b/module/utils/databases/model.mjs @@ -32,7 +32,12 @@ export const numberBucketSchema = Joi.object({ export const stringBucketSchema = Joi.object({ type: Joi.string().valid(`string`).required(), - choices: Joi.array().items(Joi.string().trim().invalid(``)).optional(), + choices: Joi + .array() + .items( + Joi.string().trim().invalid(``), + ) + .optional(), }); // MARK: Graphs From 12ac7edbc50564d4f932784f2f7a8e07c4f6f6b4 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 25 May 2025 18:08:18 -0600 Subject: [PATCH 04/77] Add workflow to build the release --- github/workflows/draft-release.yaml | 51 +++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 github/workflows/draft-release.yaml diff --git a/github/workflows/draft-release.yaml b/github/workflows/draft-release.yaml new file mode 100644 index 0000000..b3f9b77 --- /dev/null +++ b/github/workflows/draft-release.yaml @@ -0,0 +1,51 @@ +name: Create Draft Release +on: [workflow_dispatch] +jobs: + everything: + runs-on: ubuntu-latest + steps: + # Checkout the repository + - uses: actions/checkout@v4 + + # Install node and NPM + - uses: actions/setup-node@v4 + with: + node-version: "19" + + # Install required packages + - run: npm install + - run: npm run build + + - name: Reading the system.json for the version + id: "version" + run: cat prod.dist/system.json | echo version=`jq -r ".version"` >> "$GITHUB_OUTPUT" + + # Check that tag doesn't exist + - uses: mukunku/tag-exists-action@v1.5.0 + id: check-tag + with: + tag: "v${{ steps.version.outputs.version }}" + + - name: "Ensure that the tag doesn't exist" + if: ${{ steps.check-tag.outputs.exists == 'true' }} + run: exit 1 + + - name: Move the manifest to a temp file + id: manifest-move + run: mv prod.dist/module.json prod.dist/module.temp.json + + - name: Update the download property in the manifest + id: manifest-update + run: cat prod.dist/module.temp.json | jq -r --tab '.download = "https://github.com/${{ github.repository }}/releases/download/v${{ steps.version.outputs.version }}/release.zip"' > prod.dist/module.json + + - name: Create the zip + run: zip -r release.zip prod.dist/* + + - name: Create the draft release + uses: ncipollo/release-action@v1 + with: + tag: "v${{ steps.version.outputs.version }}" + commit: ${{ github.ref }} + draft: true + generateReleaseNotes: true + artifacts: "release.zip,prod.dist/module.json" \ No newline at end of file From 9b3751b28185af30d320bbdd913dae6c1d9a9dde Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 25 May 2025 18:21:29 -0600 Subject: [PATCH 05/77] Add a user setting to determine if the sidebar tab should be added --- module/hooks/init.mjs | 31 ++++++++++++++++++------------- module/settings/user.mjs | 11 +++++++++++ public/langs/en-ca.json | 4 ++++ 3 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 module/settings/user.mjs diff --git a/module/hooks/init.mjs b/module/hooks/init.mjs index 1422bce..e7e1d2d 100644 --- a/module/hooks/init.mjs +++ b/module/hooks/init.mjs @@ -14,26 +14,31 @@ import helpers from "../handlebarsHelpers/_index.mjs"; import { Logger } from "../utils/Logger.mjs"; import { registerCustomComponents } from "../Apps/elements/_index.mjs"; import { registerMetaSettings } from "../settings/meta.mjs"; +import { registerUserSettings } from "../settings/user.mjs"; import { registerWorldSettings } from "../settings/world.mjs"; Hooks.on(`init`, () => { Logger.debug(`Initializing`); - // Add a custom sidebar tab for the module - CONFIG.ui.sidebar.TABS.stats = { - active: false, - icon: `fa-solid fa-chart-line`, - tooltip: `Stats!`, - }; - CONFIG.ui.stats = StatSidebar; - - // Inject the tab right before settings; - const temp = CONFIG.ui.sidebar.TABS.settings; - delete CONFIG.ui.sidebar.TABS.settings; - CONFIG.ui.sidebar.TABS.settings = temp; - registerMetaSettings(); registerWorldSettings(); + registerUserSettings(); + + // Add a custom sidebar tab for the module + if (game.settings.get(__ID__, `statsSidebarTab`)) { + CONFIG.ui.sidebar.TABS.stats = { + active: false, + icon: `fa-solid fa-chart-line`, + tooltip: `Stats!`, + }; + CONFIG.ui.stats = StatSidebar; + + // Inject the custom tab right before settings + const temp = CONFIG.ui.sidebar.TABS.settings; + delete CONFIG.ui.sidebar.TABS.settings; + CONFIG.ui.sidebar.TABS.settings = temp; + }; + CONFIG.stats = { db: UserFlagDatabase, diff --git a/module/settings/user.mjs b/module/settings/user.mjs new file mode 100644 index 0000000..41f384d --- /dev/null +++ b/module/settings/user.mjs @@ -0,0 +1,11 @@ +export function registerUserSettings() { + game.settings.register(__ID__, `statsSidebarTab`, { + name: `STAT_TRACKER.settings.statsSidebarTab.name`, + hint: `STAT_TRACKER.settings.statsSidebarTab.hint`, + scope: `user`, + type: Boolean, + config: true, + default: true, + requiresReload: true, + }); +}; diff --git a/public/langs/en-ca.json b/public/langs/en-ca.json index 7b30eea..18bc399 100644 --- a/public/langs/en-ca.json +++ b/public/langs/en-ca.json @@ -14,6 +14,10 @@ "globalAPI": { "name": "Global API", "hint": "Whether or not the module provides a global interface for interacting with the module's backend. This is convenient for macros and using the dev console." + }, + "statsSidebarTab": { + "name": "Stats Sidebar Tab", + "hint": "Adds a custom sidebar tab to view and control the module with ease. With the sidebar tab disabled the only way to control the module is via the public API." } } } From 4b78ab4d014e712026b8e81cab82a4ff4a3d8380 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 27 May 2025 01:53:55 -0600 Subject: [PATCH 06/77] Begin work on the English documentation in a compendium --- .gitignore | 7 + package-lock.json | 881 ++++++++++++++++++ package.json | 1 + .../_source/English_pBOyeBDuTeowuDOE.json | 435 +++++++++ public/module.json | 9 + scripts/buildCompendia.mjs | 32 + scripts/extractCompendia.mjs | 27 + 7 files changed, 1392 insertions(+) create mode 100644 packs/docs/_source/English_pBOyeBDuTeowuDOE.json create mode 100644 scripts/buildCompendia.mjs create mode 100644 scripts/extractCompendia.mjs diff --git a/.gitignore b/.gitignore index 775a33f..8878028 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ +# Ignore all of the binaries and stuff that gets built for Foundry from the raw +# JSON data because it's annoying seeing it in my git changes when it isn't actually +# needed. +/packs/**/* +!/packs/**/*/ +!/packs/**/*.json + # Logs logs *.log diff --git a/package-lock.json b/package-lock.json index 9aa4a5d..7664dfa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "joi": "^17.13.3" }, "devDependencies": { + "@foundryvtt/foundryvtt-cli": "^1.1.0", "@stylistic/eslint-plugin": "^4.2.0", "eslint": "^9.25.0", "glob": "^11.0.1", @@ -590,6 +591,39 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@foundryvtt/foundryvtt-cli": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@foundryvtt/foundryvtt-cli/-/foundryvtt-cli-1.1.0.tgz", + "integrity": "sha512-ergKZDUSgQ79168r38ORyN4v/UTliA40rxElaUh5iS27Qw9H8Ep/ll8j3/HfiikO3XUDwYxZLfDJfbcyj2i9TQ==", + "dev": true, + "dependencies": { + "chalk": "^5.4.1", + "classic-level": "^1.4.1", + "esm": "^3.2.25", + "js-yaml": "^4.1.0", + "mkdirp": "^3.0.1", + "nedb-promises": "^6.2.3", + "yargs": "^17.7.2" + }, + "bin": { + "fvtt": "fvtt.mjs" + }, + "engines": { + "node": ">17.0.0" + } + }, + "node_modules/@foundryvtt/foundryvtt-cli/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -1039,6 +1073,23 @@ "win32" ] }, + "node_modules/@seald-io/binary-search-tree": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@seald-io/binary-search-tree/-/binary-search-tree-1.0.3.tgz", + "integrity": "sha512-qv3jnwoakeax2razYaMsGI/luWdliBLHTdC6jU55hQt1hcFqzauH/HsBollQ7IR4ySTtYhT+xyHoijpA16C+tA==", + "dev": true + }, + "node_modules/@seald-io/nedb": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@seald-io/nedb/-/nedb-4.1.1.tgz", + "integrity": "sha512-u7fVfzKQ/3ZaIOnYQONf2lPZtGUeQtMPjfcaQkCw/GZv5dzn20qKW6sfN0NkVbr0ksJMlWcFXNGcXYsQSb1a1g==", + "dev": true, + "dependencies": { + "@seald-io/binary-search-tree": "^1.0.3", + "localforage": "^1.9.0", + "util": "^0.12.4" + } + }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -1184,6 +1235,24 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/abstract-level": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.4.tgz", + "integrity": "sha512-eUP/6pbXBkMbXFdx4IH2fVgvB7M0JvR7/lIL33zcs0IBcwjdzSSl31TOJsaCzmKSSDF9h8QYSOJux4Nd4YJqFg==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "catering": "^2.1.0", + "is-buffer": "^2.0.5", + "level-supports": "^4.0.0", + "level-transcoder": "^1.0.1", + "module-error": "^1.0.1", + "queue-microtask": "^1.2.3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/acorn": { "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", @@ -1254,12 +1323,47 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "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 }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1281,12 +1385,83 @@ "node": ">=8" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "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 }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1296,6 +1471,15 @@ "node": ">=6" } }, + "node_modules/catering": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", + "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1323,6 +1507,95 @@ "pnpm": ">=8" } }, + "node_modules/classic-level": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.4.1.tgz", + "integrity": "sha512-qGx/KJl3bvtOHrGau2WklEZuXhS3zme+jf+fsu6Ej7W7IP/C49v7KNlWIsT1jZu0YnfzSIYDGcEWpCa1wKGWXQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.0", + "module-error": "^1.0.1", + "napi-macros": "^2.2.2", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=12" + } + }, + "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, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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 + }, + "node_modules/cliui/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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, + "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/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1390,6 +1663,37 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1402,6 +1706,36 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "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, + "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, + "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, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", @@ -1442,6 +1776,15 @@ "@esbuild/win32-x64": "0.25.2" } }, + "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, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1564,6 +1907,15 @@ "node": "*" } }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -1751,6 +2103,21 @@ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -1781,6 +2148,61 @@ "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, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "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, + "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-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, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", @@ -1843,6 +2265,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1852,6 +2286,77 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "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, + "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, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1861,6 +2366,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -1886,6 +2397,63 @@ "node": ">=0.8.19" } }, + "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 + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1904,6 +2472,24 @@ "node": ">=8" } }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1925,6 +2511,39 @@ "node": ">=0.12.0" } }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1997,6 +2616,28 @@ "json-buffer": "3.0.1" } }, + "node_modules/level-supports": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", + "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/level-transcoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", + "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "module-error": "^1.0.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2010,6 +2651,24 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dev": true, + "dependencies": { + "lie": "3.1.1" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2040,6 +2699,15 @@ "node": "20 || >=22" } }, + "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, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2098,6 +2766,30 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/module-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", + "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2122,12 +2814,38 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-macros": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", + "integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==", + "dev": true + }, "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 }, + "node_modules/nedb-promises": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/nedb-promises/-/nedb-promises-6.2.3.tgz", + "integrity": "sha512-enq0IjNyBz9Qy9W/QPCcLGh/QORGBjXbIeZeWvIjO3OMLyAvlKT3hiJubP2BKEiFniUlR3L01o18ktqgn5jxqA==", + "dev": true, + "dependencies": { + "@seald-io/nedb": "^4.0.2" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -2245,6 +2963,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -2311,6 +3038,15 @@ } ] }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2392,6 +3128,23 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", @@ -2404,6 +3157,23 @@ "node": ">=10" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2678,6 +3448,19 @@ "punycode": "^2.1.0" } }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "node_modules/vite": { "version": "6.3.2", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.2.tgz", @@ -2767,6 +3550,27 @@ "node": ">= 8" } }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -2864,6 +3668,83 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "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, + "engines": { + "node": ">=10" + } + }, + "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, + "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, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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 + }, + "node_modules/yargs/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 2008f04..2584d72 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "build": "NODE_ENV=production vite build --mode prod" }, "devDependencies": { + "@foundryvtt/foundryvtt-cli": "^1.1.0", "@stylistic/eslint-plugin": "^4.2.0", "eslint": "^9.25.0", "glob": "^11.0.1", diff --git a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json new file mode 100644 index 0000000..751a303 --- /dev/null +++ b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json @@ -0,0 +1,435 @@ +{ + "name": "English", + "_id": "pBOyeBDuTeowuDOE", + "pages": [ + { + "sort": 100000, + "name": "Getting Started", + "type": "text", + "category": "mrZHFR2i0MYp7aaY", + "_id": "xcR48pakEm49hbc2", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "

Thank you for install Stat Tracker!

This module aims to provide a clean way of keeping track of any data points you want within Foundry, whether that be dice rolls, or other things like how many natural 1s to natural 20s you get.

I was inspired by the dicestats module, however it only allows tracking dice statistics, which is something I found myself needing to work around, so I decided to make this module to fill that gap.

You've already done the hardest part of set up for this module to work on the basic level, installing it! However if you want a more advanced set up for the module, this Journal is your go-to for information.

This module makes use of multiple key terms which are important for your understanding, the main terms you need to understand are:

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{Table}

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{Row}

  • and, @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{Bucket}

You can find information about what each of those terms mean within the FAQ section of the documentation or by clicking the above links.

" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748328891633, + "modifiedTime": 1748329657624, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.xcR48pakEm49hbc2" + }, + { + "sort": 125000, + "name": "What's a \"Table\"", + "type": "text", + "category": "ZPAbuPbVOLWh75hL", + "_id": "ugzCCxQskUSYMZR4", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "

Whenever you see the term \"Table\" in relation to this module, it is indicating a group of @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{rows} that are all presented together in graphs. The table is also where graph and @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{bucket} configuration is stored.

" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748329356680, + "modifiedTime": 1748329828806, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.ugzCCxQskUSYMZR4" + }, + { + "sort": 112500, + "name": "What's a \"Row\"", + "type": "text", + "category": "ZPAbuPbVOLWh75hL", + "_id": "S7Z6mZ0JablJVQJu", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748329454864, + "modifiedTime": 1748329832738, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.S7Z6mZ0JablJVQJu" + }, + { + "sort": 137500, + "name": "What's a \"Bucket\"", + "type": "text", + "category": "ZPAbuPbVOLWh75hL", + "_id": "e9FYKidbfFnnTspO", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748329573212, + "modifiedTime": 1748329664880, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.e9FYKidbfFnnTspO" + }, + { + "sort": 237500, + "name": "The Sidebar Tab", + "type": "text", + "category": "mrZHFR2i0MYp7aaY", + "_id": "ZI6rVlgXYnZGZ3MS", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "

This stat tracker comes with a custom sidebar tab that provides a general overview of the module's state as well as short-cuts for opening and controlling settings of the module.

The primary actions within the sidebar are:

  • Viewing the data in graphs

  • Creating a new @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{table} (Gamemasters only)

  • Managing existing @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{tables} (Gamemasters only)

" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748329854358, + "modifiedTime": 1748330869938, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.ZI6rVlgXYnZGZ3MS" + }, + { + "sort": 437500, + "name": "Player Settings", + "type": "text", + "category": "mrZHFR2i0MYp7aaY", + "_id": "GgrPHfU09UuJl8W8", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748330739278, + "modifiedTime": 1748330769327, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.GgrPHfU09UuJl8W8" + }, + { + "sort": 337500, + "name": "Gamemaster Settings", + "type": "text", + "category": "mrZHFR2i0MYp7aaY", + "_id": "UuAWTQtd3QMKOWvU", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1 + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": 0, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748330762378, + "modifiedTime": 1748330780668, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.UuAWTQtd3QMKOWvU" + }, + { + "sort": 537500, + "name": "Utilities", + "type": "text", + "category": "KGdeJUfatQ9v0raI", + "_id": "TQzWrVTEz4oQhLPD", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "

The module provides a multitude of utility functions through it's API for usage however desired. This will go over them and describe their purpose.

filterPrivateRows

This method is intended to take @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{rows} provided by the database and filter out any that the user would not be able to see normally. This is usually called by the database adapters so there's unlikely to be any reason to use it externally.

Available under <api>.utils.filterPrivateRows.

validateValue

Available under <api>.utils.validateValue.

validateBucketConfig

Available under <api>.utils.validateBucketConfig.

" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748330904988, + "modifiedTime": 1748331608766, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.TQzWrVTEz4oQhLPD" + }, + { + "sort": 337500, + "name": "Database Adapters", + "type": "text", + "category": "KGdeJUfatQ9v0raI", + "_id": "PcdmuLgNM15h0in1", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "

This is a list of all available database adapters and how they're configured.

Database

This database adapter isn't a full adapter, this is an abstract class that is used by the other database adapters to enforce a consistent method / interface specification. The general interface includes implementation details for storing the table data in a world setting as well as adding/removing any applications that are rendered as part of the module's operations.

Available under <api>.databases.Database.

UserFlagDatabase

This database adapter uses Foundry's flag system in order to store the row data in the User document, leveraging Foundry's automatic database update propagation to other clients. The application handling and table storage utilizes the abstract implementations.

Available under <api>.databases.UserFlagDatabase.

MemoryDatabase

This database adapter should not be used in any actual games, it is intended for development only.

Available under <api>.databases.MemoryDatabase.

" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748331161024, + "modifiedTime": 1748331570983, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.PcdmuLgNM15h0in1" + } + ], + "folder": null, + "categories": [ + { + "name": "Overview", + "sort": 100000, + "_id": "mrZHFR2i0MYp7aaY", + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748328842906, + "modifiedTime": 1748328842906, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.categories!pBOyeBDuTeowuDOE.mrZHFR2i0MYp7aaY" + }, + { + "name": "API", + "sort": 300000, + "_id": "KGdeJUfatQ9v0raI", + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748328851997, + "modifiedTime": 1748328851997, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.categories!pBOyeBDuTeowuDOE.KGdeJUfatQ9v0raI" + }, + { + "name": "FAQ", + "sort": 200000, + "_id": "ZPAbuPbVOLWh75hL", + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748329482648, + "modifiedTime": 1748329482648, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.categories!pBOyeBDuTeowuDOE.ZPAbuPbVOLWh75hL" + } + ], + "sort": 0, + "ownership": { + "default": 0, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": { + "core": { + "locked": false + } + }, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748328832096, + "modifiedTime": 1748330787690, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal!pBOyeBDuTeowuDOE" +} diff --git a/public/module.json b/public/module.json index 0894968..ca51352 100644 --- a/public/module.json +++ b/public/module.json @@ -22,5 +22,14 @@ "name": "English (Canadian)", "path": "langs/en-ca.json" } + ], + "packs": [ + { + "name": "docs", + "label": "Documentation", + "module": "stat-tracker", + "type": "JournalEntry", + "path": "packs/docs" + } ] } diff --git a/scripts/buildCompendia.mjs b/scripts/buildCompendia.mjs new file mode 100644 index 0000000..ea0cae0 --- /dev/null +++ b/scripts/buildCompendia.mjs @@ -0,0 +1,32 @@ +import { existsSync } from "fs"; +import { readFile } from "fs/promises"; +import { join } from "path"; +import { compilePack } from "@foundryvtt/foundryvtt-cli"; + +async function main() { + const manifest = JSON.parse(await readFile(`./public/module.json`, `utf-8`)); + + if (!manifest.packs || manifest.packs.length === 0) { + console.log(`No compendium packs defined`); + process.exit(0); + }; + + for (const compendium of manifest.packs) { + console.debug(`Packing ${compendium.label} (${compendium.name})`); + let src = join(process.cwd(), compendium.path, `_source`); + if (!existsSync(src)) { + console.warn(`${compendium.path} doesn't exist, skipping.`) + continue; + }; + await compilePack( + src, + join(process.cwd(), compendium.path), + { recursive: true }, + ); + console.debug(`Finished packing ${compendium.name}`); + }; + + console.log(`Finished packing compendia`) +}; + +main(); diff --git a/scripts/extractCompendia.mjs b/scripts/extractCompendia.mjs new file mode 100644 index 0000000..939c31d --- /dev/null +++ b/scripts/extractCompendia.mjs @@ -0,0 +1,27 @@ +import { readFile } from "fs/promises"; +import { join } from "path"; +import { extractPack } from "@foundryvtt/foundryvtt-cli"; + +async function main() { + const manifest = JSON.parse(await readFile(`./public/module.json`, `utf-8`)); + + if (!manifest.packs || manifest.packs.length === 0) { + console.log(`No compendium packs defined`); + process.exit(0); + }; + + for (const compendium of manifest.packs) { + console.debug(`Unpacking ${compendium.label} (${compendium.name})`); + let src = join(process.cwd(), compendium.path, `_source`); + await extractPack( + join(process.cwd(), compendium.path), + src, + { recursive: true }, + ); + console.debug(`Finished packing ${compendium.name}`); + }; + + console.log(`Finished unpacking compendia`); +}; + +main(); From a35462983915812ec8b4fb30ab70afd97ca20385 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 27 May 2025 23:29:06 -0600 Subject: [PATCH 07/77] Update the English documentation --- .../_source/English_pBOyeBDuTeowuDOE.json | 286 ++++++++++++++---- 1 file changed, 221 insertions(+), 65 deletions(-) diff --git a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json index 751a303..34379aa 100644 --- a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json +++ b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json @@ -16,7 +16,7 @@ "image": {}, "text": { "format": 1, - "content": "

Thank you for install Stat Tracker!

This module aims to provide a clean way of keeping track of any data points you want within Foundry, whether that be dice rolls, or other things like how many natural 1s to natural 20s you get.

I was inspired by the dicestats module, however it only allows tracking dice statistics, which is something I found myself needing to work around, so I decided to make this module to fill that gap.

You've already done the hardest part of set up for this module to work on the basic level, installing it! However if you want a more advanced set up for the module, this Journal is your go-to for information.

This module makes use of multiple key terms which are important for your understanding, the main terms you need to understand are:

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{Table}

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{Row}

  • and, @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{Bucket}

You can find information about what each of those terms mean within the FAQ section of the documentation or by clicking the above links.

" + "content": "

Thank you for installing Stat Tracker!

This module aims to provide a clean way of keeping track of any data points you want within Foundry, whether that be dice rolls, or other things like how many natural 1s to natural 20s you get.

I was inspired by the dicestats module, however it only allows tracking dice statistics, which is something I found myself needing to work around, so I decided to make this module to fill that gap.

You've already done the hardest part of set up for this module to work on the basic level, installing it! However if you want a more advanced set up for the module, this Journal is your go-to for information.

This module makes use of multiple key terms which are important for your understanding, the main terms you need to understand are:

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{Row}

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{Table}

  • and, @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{Bucket}

You can find information about what each of those terms mean within the \"Key Concepts\" section of the documentation or by clicking the above links.

" }, "video": { "controls": true, @@ -36,14 +36,14 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748328891633, - "modifiedTime": 1748329657624, + "modifiedTime": 1748406648943, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.xcR48pakEm49hbc2" }, { - "sort": 125000, - "name": "What's a \"Table\"", + "sort": 300000, + "name": "Table", "type": "text", "category": "ZPAbuPbVOLWh75hL", "_id": "ugzCCxQskUSYMZR4", @@ -55,7 +55,7 @@ "image": {}, "text": { "format": 1, - "content": "

Whenever you see the term \"Table\" in relation to this module, it is indicating a group of @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{rows} that are all presented together in graphs. The table is also where graph and @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{bucket} configuration is stored.

" + "content": "

Tables are quite close to being a \"container\" for every piece of data within the module, every @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{row} within the module must be associated with a particular table. The table is responsible for maintaining all of the @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{bucket} and @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.IXZpEBEJsvOpY3OL]{graph} configurations, controlling what data is allowed to be saved and how to represent that data.

You can manage almost all tables within the module by using the @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.Z31C8BPl4ZXDn3R6]{Table Manager}.

" }, "video": { "controls": true, @@ -75,14 +75,14 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748329356680, - "modifiedTime": 1748329828806, + "modifiedTime": 1748394635911, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.ugzCCxQskUSYMZR4" }, { - "sort": 112500, - "name": "What's a \"Row\"", + "sort": 200000, + "name": "Row", "type": "text", "category": "ZPAbuPbVOLWh75hL", "_id": "S7Z6mZ0JablJVQJu", @@ -94,7 +94,7 @@ "image": {}, "text": { "format": 1, - "content": "" + "content": "

Rows make up the vast majority of data that the stat tracker stores. Every dice roll, or custom entry, is saved as a single row.

Each row consists of the following data:

  • The value (e.g. the result of a single dice roll)

  • A timestamp of when it was added to the database

  • A privacy mode (one for each of the Foundry-provided roll modes, see @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.WYaZPgSRDx8L7Zmq#privacy-modes]{Privacy Modes})

  • An identifier

This combination of data can end up becoming a lot of data to load when opening Foundry, if you don't think you'll be resetting the data very often, please consider checking out the different @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.PcdmuLgNM15h0in1]{Database Adapters} and picking one of them that is most suitable for your campaign.

" }, "video": { "controls": true, @@ -114,14 +114,14 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748329454864, - "modifiedTime": 1748329832738, + "modifiedTime": 1748394635911, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.S7Z6mZ0JablJVQJu" }, { - "sort": 137500, - "name": "What's a \"Bucket\"", + "sort": 400000, + "name": "Bucket", "type": "text", "category": "ZPAbuPbVOLWh75hL", "_id": "e9FYKidbfFnnTspO", @@ -153,13 +153,13 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748329573212, - "modifiedTime": 1748329664880, + "modifiedTime": 1748394635911, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.e9FYKidbfFnnTspO" }, { - "sort": 237500, + "sort": 500000, "name": "The Sidebar Tab", "type": "text", "category": "mrZHFR2i0MYp7aaY", @@ -192,53 +192,14 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748329854358, - "modifiedTime": 1748330869938, + "modifiedTime": 1748394635911, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.ZI6rVlgXYnZGZ3MS" }, { - "sort": 437500, - "name": "Player Settings", - "type": "text", - "category": "mrZHFR2i0MYp7aaY", - "_id": "GgrPHfU09UuJl8W8", - "system": {}, - "title": { - "show": true, - "level": 1 - }, - "image": {}, - "text": { - "format": 1, - "content": "" - }, - "video": { - "controls": true, - "volume": 0.5 - }, - "src": null, - "ownership": { - "default": -1, - "t2sWGWEYSMFrfBu3": 3 - }, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.344", - "systemId": "empty-system", - "systemVersion": "0.0.0", - "createdTime": 1748330739278, - "modifiedTime": 1748330769327, - "lastModifiedBy": "t2sWGWEYSMFrfBu3" - }, - "_key": "!journal.pages!pBOyeBDuTeowuDOE.GgrPHfU09UuJl8W8" - }, - { - "sort": 337500, - "name": "Gamemaster Settings", + "sort": 600000, + "name": "Configuration", "type": "text", "category": "mrZHFR2i0MYp7aaY", "_id": "UuAWTQtd3QMKOWvU", @@ -249,7 +210,8 @@ }, "image": {}, "text": { - "format": 1 + "format": 1, + "content": "

The module provides a bunch of settings to be able to control how it interacts with Foundry in various ways. Each setting has a description provided in the settings configuration window, but these descriptions will go more in depth than the ones in there.

Roll Auto-Tracking

This tells the module to automatically track rolls that are sent to the chat, this includes systems, modules, and other rolls like RollTables. As long as it gets sent to the chat, this will allow that roll to automatically be tracked.

By default, this only tracks the standard dice sizes (d4, d6, d8, d10, d12, d20, d100), however you can add any dice size you want by creating a new table named Dice/dX where X is the number of sides the dice should have (e.g. for a 3-sided dice you would make the table name be Dice/d3). By adding a table in this way, it's configuration will be locked for editing and the only way to change it will be to delete the table entirely.

For most systems you will want to leave this setting enabled, because otherwise there is a chance that no dice rolls will be tracked at all unless the system has specifically implemented an integration with the module.

Global API

This setting is primarily targeted at users who would like to integrate stats tracking into macros, as it exposes a globally available stats variable with references to all of the exposed methods and utility helpers of the module. This can sometimes cause conflicts with systems or other modules, so make sure that there isn't already another a global variable named stats before enabling this setting.

Below is an example of how to retrieve the module's API both with and without this setting enabled:

// with it enabled:\nconst statViewer = new stats.Apps.StatsViewer;\nstatViewer.render({ force: true });\n\n// with it disabled\nconst api = game.modules.get(`stat-tracker`)?.api;\nconst statViewer = new api.Apps.StatsViewer;\nstatViewer?.render({ force: true });

Stats Sidebar Tab

" }, "video": { "controls": true, @@ -269,13 +231,13 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748330762378, - "modifiedTime": 1748330780668, + "modifiedTime": 1748408480001, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.UuAWTQtd3QMKOWvU" }, { - "sort": 537500, + "sort": 900000, "name": "Utilities", "type": "text", "category": "KGdeJUfatQ9v0raI", @@ -308,13 +270,13 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748330904988, - "modifiedTime": 1748331608766, + "modifiedTime": 1748394635911, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.TQzWrVTEz4oQhLPD" }, { - "sort": 337500, + "sort": 800000, "name": "Database Adapters", "type": "text", "category": "KGdeJUfatQ9v0raI", @@ -327,7 +289,7 @@ "image": {}, "text": { "format": 1, - "content": "

This is a list of all available database adapters and how they're configured.

Database

This database adapter isn't a full adapter, this is an abstract class that is used by the other database adapters to enforce a consistent method / interface specification. The general interface includes implementation details for storing the table data in a world setting as well as adding/removing any applications that are rendered as part of the module's operations.

Available under <api>.databases.Database.

UserFlagDatabase

This database adapter uses Foundry's flag system in order to store the row data in the User document, leveraging Foundry's automatic database update propagation to other clients. The application handling and table storage utilizes the abstract implementations.

Available under <api>.databases.UserFlagDatabase.

MemoryDatabase

This database adapter should not be used in any actual games, it is intended for development only.

Available under <api>.databases.MemoryDatabase.

" + "content": "

This is a list of all available database adapters and how they're configured.

Database

This database adapter isn't a full adapter, this is an abstract class that is used by the other database adapters to enforce a consistent method / interface specification. The general interface includes implementation details for storing the table data in a world setting as well as adding/removing any applications that are rendered as part of the module's operations.

Available under <api>.databases.Database.

User Flag Database

This database adapter uses Foundry's flag system in order to store the row data in the User document, leveraging Foundry's automatic database update propagation to other clients. The application handling and table storage utilizes the abstract implementations.

Available under <api>.databases.UserFlagDatabase.

Condensed User Flag Database

This is unimplemented at the moment. But it will be a database that makes long-term campaign storage more viable, at the tradeoff of not being able to filter data as granularly.

Memory Database

This database adapter should not be used in any actual games, it is intended for development only.

Available under <api>.databases.MemoryDatabase.

" }, "video": { "controls": true, @@ -347,10 +309,204 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748331161024, - "modifiedTime": 1748331570983, + "modifiedTime": 1748409643379, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.PcdmuLgNM15h0in1" + }, + { + "sort": 1000000, + "name": "Graph", + "type": "text", + "category": "ZPAbuPbVOLWh75hL", + "_id": "IXZpEBEJsvOpY3OL", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1 + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748393806045, + "modifiedTime": 1748394635911, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.IXZpEBEJsvOpY3OL" + }, + { + "sort": 1100000, + "name": "Table Manager", + "type": "text", + "category": "mrZHFR2i0MYp7aaY", + "_id": "Z31C8BPl4ZXDn3R6", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748393919924, + "modifiedTime": 1748394635911, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.Z31C8BPl4ZXDn3R6" + }, + { + "sort": 850000, + "name": "Enums", + "type": "text", + "category": "KGdeJUfatQ9v0raI", + "_id": "WYaZPgSRDx8L7Zmq", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "

Privacy Modes

" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748394110971, + "modifiedTime": 1748394637940, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.WYaZPgSRDx8L7Zmq" + }, + { + "sort": 1000000, + "name": "Usage Within Macros", + "type": "text", + "category": "mrZHFR2i0MYp7aaY", + "_id": "cpix3FpMmI1U1xXa", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "

This module was designed from the ground up with the desire to be able to be used within macros, because of this it has first-class Macro support through the entire API.

Getting the API

The primary API that you will interact with inside of macros is held within CONFIG.stats, this is the core API which allows interacting with the stored data however the user wants it to be stored, it allows accessing the apps to view stats, manage the tables, and create new tables.

The API Methods

Each of the items within CONFIG.stats has a different purpose, but most of the programmatic interactions for the module are most likely to be interested in the CONFIG.stats.db part of the API, which handles all of the data within the module in your configured preference.

" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748408620967, + "modifiedTime": 1748409821925, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.cpix3FpMmI1U1xXa" + }, + { + "sort": 700000, + "name": "Database Interface", + "type": "text", + "category": "KGdeJUfatQ9v0raI", + "_id": "PlKHrrb61Uc1sGbN", + "system": {}, + "title": { + "show": true, + "level": 1 + }, + "image": {}, + "text": { + "format": 1, + "content": "

This is the interface which all @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.PcdmuLgNM15h0in1]{Database Adapters} must conform to in order for the module to function. If they do not conform to this a warning will be thrown and the module will override the provided database with a database adapter which does nothing, so that the existing data will be protected from errors.

" + }, + "video": { + "controls": true, + "volume": 0.5 + }, + "src": null, + "ownership": { + "default": -1, + "t2sWGWEYSMFrfBu3": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.344", + "systemId": "empty-system", + "systemVersion": "0.0.0", + "createdTime": 1748408916163, + "modifiedTime": 1748409672979, + "lastModifiedBy": "t2sWGWEYSMFrfBu3" + }, + "_key": "!journal.pages!pBOyeBDuTeowuDOE.PlKHrrb61Uc1sGbN" } ], "folder": null, @@ -392,7 +548,7 @@ "_key": "!journal.categories!pBOyeBDuTeowuDOE.KGdeJUfatQ9v0raI" }, { - "name": "FAQ", + "name": "Key Concepts", "sort": 200000, "_id": "ZPAbuPbVOLWh75hL", "flags": {}, @@ -428,7 +584,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748328832096, - "modifiedTime": 1748330787690, + "modifiedTime": 1748393668194, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal!pBOyeBDuTeowuDOE" From 47deb24d67ef4540c4009b6d839f6adde1b298b6 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 27 May 2025 23:34:27 -0600 Subject: [PATCH 08/77] Hard-override any configured database handler that is erroneous and freeze the config after ready. --- module/hooks/ready.mjs | 13 +++++++++- module/utils/databases/NilDatabase.mjs | 34 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 module/utils/databases/NilDatabase.mjs diff --git a/module/hooks/ready.mjs b/module/hooks/ready.mjs index 38980e5..1d5e7c8 100644 --- a/module/hooks/ready.mjs +++ b/module/hooks/ready.mjs @@ -1,12 +1,23 @@ import { Database } from "../utils/databases/Database.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { NilDatabase } from "../utils/databases/NilDatabase.mjs"; Hooks.on(`ready`, () => { Logger.log(`Version: ${__VERSION__}`); // Alert GMs when the configured DB is invalid if (!(CONFIG.stats.db.prototype instanceof Database) && game.user.isGM) { - ui.notifications.error(`The database handler does not conform to the required heirarchy, the stats tracker module will almost certainly not work correctly.`, { permanent: true }); + ui.notifications.error(`The database adapter does not conform to the required specification, the stats tracker module overrode the configured database adapter with a stub to protect data that exists already.`, { permanent: true }); + CONFIG.stats.db = NilDatabase; }; + + /* + Prevent any run-time modifications to the CONFIG API so that users can't wreck + themselves nor their data by fooling around with the values. + */ + if (import.meta.env.PROD) { + Object.freeze(CONFIG.stats); + }; + CONFIG.stats.db.registerListeners(); }); diff --git a/module/utils/databases/NilDatabase.mjs b/module/utils/databases/NilDatabase.mjs new file mode 100644 index 0000000..0a76981 --- /dev/null +++ b/module/utils/databases/NilDatabase.mjs @@ -0,0 +1,34 @@ +import { Database } from "./Database.mjs"; + +/** + * This database implemention is not recommended for any actual usage, + * it is intended for overriding the current database implementation + * when a non-conforming Database is provided as the CONFIG.stats.db + * value in order to maintain the API interface for dependant modules + * and systems. + */ +export class NilDatabase extends Database { + // MARK: Table Ops + static async createTable() {}; + static async getTables() {}; + static async getTable() {}; + static async updateTable() {}; + static async deleteTable() {}; + + // MARK: Row Ops + static async createRow() {}; + static async createRows() {}; + static async getRows() {}; + static async updateRow() {}; + static async deleteRow() {}; + + // MARK: Applications + static addApp() {}; + static removeApp() {}; + static async render() {}; + + // MARK: Listeners + static async registerListeners() {}; + static async triggerListeners() {}; + static async unregisterListeners() {}; +}; From 2567f5fb62ba5d850fffcb2bc3b3e27a1552333a Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 27 May 2025 23:37:31 -0600 Subject: [PATCH 09/77] Add a build step in dev mode to create a symlink to the compendium packs on disk --- vite.config.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/vite.config.js b/vite.config.js index f2b5aca..ed76b76 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,9 +1,9 @@ /* eslint-disable no-undef */ +import { readFileSync, symlinkSync } from "fs"; import { defineConfig } from "vite"; import { glob } from "glob"; import path from "path"; -import { readFileSync } from "fs"; // MARK: custom plugins function fileMarkerPlugin() { @@ -41,6 +41,24 @@ function watcher(...globs) { }; }; +/* +The intent of this plugin is to handle the symlinking of the compendium packs +so that they can modified during dev without needing to worry about the rebuild +destroying the in-progress compendia data. +*/ +function handlePacks() { + return { + writeBundle(options) { + console.log(options.dir); + symlinkSync( + path.resolve(__dirname, `packs`), + `${options.dir}/packs`, + `dir`, + ); + }, + }; +}; + // MARK: config export default defineConfig(({ mode }) => { const isProd = mode === `prod`; @@ -49,6 +67,7 @@ export default defineConfig(({ mode }) => { if (!isProd) { plugins.push( + handlePacks(), watcher( `./public`, ), From 1e007af52a0f94bb126b568e7bec8bebf4200118 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 29 May 2025 01:22:17 -0600 Subject: [PATCH 10/77] Add an API interface for performing DB migrations as required --- eslint.config.mjs | 1 + module/hooks/ready.mjs | 32 ++++++++++++++++++++++ module/settings/meta.mjs | 7 +++++ module/utils/databases/Database.mjs | 37 ++++++++++++++++++++++++++ module/utils/databases/NilDatabase.mjs | 5 ++++ vite.config.js | 1 + 6 files changed, 83 insertions(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index ea4be48..dd8bf28 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -45,6 +45,7 @@ export default [ }, languageOptions: { globals: { + __TITLE__: `readonly`, __ID__: `readonly`, __VERSION__: `readonly`, }, diff --git a/module/hooks/ready.mjs b/module/hooks/ready.mjs index 1d5e7c8..b9d5871 100644 --- a/module/hooks/ready.mjs +++ b/module/hooks/ready.mjs @@ -11,6 +11,38 @@ Hooks.on(`ready`, () => { CONFIG.stats.db = NilDatabase; }; + /* + Perform any required data migration if any is required for the version + jump that the user may have caused. This only migrates the data iff the + currently authenticated user is able to perform the full migration of + data. + */ + const db = CONFIG.stats.db; + const lastVersion = game.settings.get(__ID__, `lastVersion`); + const canDoMigration = db.canPerformMigration(); + const requiresMigration = db.requiresMigrationFrom(lastVersion); + if (requiresMigration) { + if (canDoMigration) { + const notif = ui.notifications.info( + `${__TITLE__} | Performing data migration, please do not close the window`, + { progress: true, permanent: true }, + ); + + // Fire and forget + CONFIG.stats.db.migrateData(notif) + .then(() => { + game.settings.set(__ID__, __VERSION__); + setTimeout(() => ui.notifications.remove(notif), 500); + }); + } else { + ui.notifications.error( + `The stat-tracker database is out of date, temporarily disabling the stat-tracker module's functionality until the migration can be performed by a GM user logging into the world.`, + { console: false, permanent: true }, + ); + CONFIG.stats.db = NilDatabase; + }; + }; + /* Prevent any run-time modifications to the CONFIG API so that users can't wreck themselves nor their data by fooling around with the values. diff --git a/module/settings/meta.mjs b/module/settings/meta.mjs index c236f7e..63e1b48 100644 --- a/module/settings/meta.mjs +++ b/module/settings/meta.mjs @@ -23,4 +23,11 @@ export function registerMetaSettings() { config: false, requiresReload: false, }); + + game.settings.register(__ID__, `lastVersion`, { + scope: `world`, + type: String, + config: false, + requiresReload: false, + }); }; diff --git a/module/utils/databases/Database.mjs b/module/utils/databases/Database.mjs index 8185b8f..65aa285 100644 --- a/module/utils/databases/Database.mjs +++ b/module/utils/databases/Database.mjs @@ -220,6 +220,43 @@ export class Database { static async triggerListeners() {}; static async unregisterListeners() {}; + + // MARK: Migrations + /** + * Determines if the currently authenticated user is capable of running + * the full migration on their own. + * + * @returns {boolean} + */ + static async canPerformMigration() { + // TODO: this *must* account for isActiveGM, because otherwise the + // world setting cannot be updated after the migration finishes. + return game.user.isActiveGM; + }; + + /** + * Determines if the previous version of the plugin that was active + * needs to be migrated in order to work with the new version. + * + * @param {string} lastVersion The version that was last active + * @returns {boolean} + */ + static async requiresMigrationFrom(lastVersion) { + return foundry.utils.isNewerVersion(__VERSION__, lastVersion); + }; + + /** + * This method migrates ALL of the database data from one version of + * the module to the currently installed module. This is not guaranteed + * to run only on one client, so it should be made to be either + * idempotent, or have an operation locking mechanism that can prevent + * other clients from executing it if there's a migration in-progress. + * + * @param {string} lastVersion The last version that the user had active + * @param {Notification} notif The progress bar notification used for + * user feedback while performing migrations. + */ + static async migrateData(lastVersion, notif) {}; }; /* eslint-enable no-unused-vars */ diff --git a/module/utils/databases/NilDatabase.mjs b/module/utils/databases/NilDatabase.mjs index 0a76981..c526a2d 100644 --- a/module/utils/databases/NilDatabase.mjs +++ b/module/utils/databases/NilDatabase.mjs @@ -31,4 +31,9 @@ export class NilDatabase extends Database { static async registerListeners() {}; static async triggerListeners() {}; static async unregisterListeners() {}; + + // MARK: Migrations + static async canPerformMigration() { return true }; + static async requiresMigrationFrom() { return false }; + static async migrateData() {}; }; diff --git a/vite.config.js b/vite.config.js index ed76b76..d849e07 100644 --- a/vite.config.js +++ b/vite.config.js @@ -80,6 +80,7 @@ export default defineConfig(({ mode }) => { return { plugins, define: { + __TITLE__: JSON.stringify(manifest.title), __ID__: JSON.stringify(manifest.id), __VERSION__: JSON.stringify(manifest.version), }, From 0af4be2cfb71a61ffc2ef718c441455e227b828c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 29 May 2025 01:23:42 -0600 Subject: [PATCH 11/77] Update documentation Journal --- packs/docs/_source/English_pBOyeBDuTeowuDOE.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json index 34379aa..18863de 100644 --- a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json +++ b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json @@ -94,7 +94,7 @@ "image": {}, "text": { "format": 1, - "content": "

Rows make up the vast majority of data that the stat tracker stores. Every dice roll, or custom entry, is saved as a single row.

Each row consists of the following data:

  • The value (e.g. the result of a single dice roll)

  • A timestamp of when it was added to the database

  • A privacy mode (one for each of the Foundry-provided roll modes, see @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.WYaZPgSRDx8L7Zmq#privacy-modes]{Privacy Modes})

  • An identifier

This combination of data can end up becoming a lot of data to load when opening Foundry, if you don't think you'll be resetting the data very often, please consider checking out the different @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.PcdmuLgNM15h0in1]{Database Adapters} and picking one of them that is most suitable for your campaign.

" + "content": "

Rows make up the vast majority of data that the stat tracker stores. Every dice roll, or custom entry, is saved as a single row.

Each row consists of the following data:

  • The value (e.g. the result of a single dice roll)

  • A timestamp of when it was added to the database

  • A privacy mode (one for each of the Foundry-provided roll modes, see @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.WYaZPgSRDx8L7Zmq#privacy-modes]{Privacy Modes})

  • An identifier

This combination of data can end up becoming a lot of data to load when opening Foundry, if you don't think you'll be resetting the data very often, please consider checking out the different @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.PcdmuLgNM15h0in1]{Database Adapters} and picking one of them that is more suitable for your campaign.

" }, "video": { "controls": true, @@ -114,7 +114,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748329454864, - "modifiedTime": 1748394635911, + "modifiedTime": 1748497975724, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.S7Z6mZ0JablJVQJu" @@ -133,7 +133,7 @@ "image": {}, "text": { "format": 1, - "content": "" + "content": "

A bucket is the term used to identify a group of allowed values within a @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{table}, each bucket must have a type, and a number of additional settings depending on what type it is.

String Buckets

This is the most simple type of bucket, it allows a string to be added as the row's value. The only additional configuration for this type of bucket is restricting what strings can be added be added.

e.g. you can limit each row to only have a value of \"Critical Success\", or \"Critical Failure\" and if someone tries to add \"Apple Sauce\" into the table, it will reject that row.

Number Buckets

This type of bucket is likely the one you will utilize the most, it allows storing any number. It accepts an set of additional options described below, all of which are optional.

Setting

Description

Minimum

The minimum allowed value

Maximum

The maximum allowed value, must be greater than Minimum

Step

Requires Minimum

When a step is set it requires each number to be a \"step\" away from the lower one. So if you have a minimum of 2 and a step of 4, the allowed values are: 2, 6, 10, 14, 18, etc.

Range Buckets

The range bucket is what is used by the auto-generated dice tables, these bucket types use the same options as the @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO#number-buckets]{Number Buckets} but the Minimum/Maximum/Step options are required when the type is range.

" }, "video": { "controls": true, @@ -153,7 +153,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748329573212, - "modifiedTime": 1748394635911, + "modifiedTime": 1748499573438, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.e9FYKidbfFnnTspO" @@ -327,7 +327,8 @@ }, "image": {}, "text": { - "format": 1 + "format": 1, + "content": "

Right now all graphs are bar graphs and non-configurable. This is coming in a later update.

" }, "video": { "controls": true, @@ -347,7 +348,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748393806045, - "modifiedTime": 1748394635911, + "modifiedTime": 1748499640505, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.IXZpEBEJsvOpY3OL" From 0a01528bd322082dcfa7a100e7aacb799fe8f0c7 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 29 May 2025 22:49:35 -0600 Subject: [PATCH 12/77] Set the correct setting name instead of trying to set a setting with the version name --- module/hooks/ready.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/hooks/ready.mjs b/module/hooks/ready.mjs index b9d5871..33fe483 100644 --- a/module/hooks/ready.mjs +++ b/module/hooks/ready.mjs @@ -31,7 +31,7 @@ Hooks.on(`ready`, () => { // Fire and forget CONFIG.stats.db.migrateData(notif) .then(() => { - game.settings.set(__ID__, __VERSION__); + game.settings.set(__ID__, `lastVersion`, __VERSION__); setTimeout(() => ui.notifications.remove(notif), 500); }); } else { From 7c68dd0c319f330da5bc94bf82c700077de28df2 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 29 May 2025 23:15:46 -0600 Subject: [PATCH 13/77] Disable requiresReload on autoTrackRolls --- module/settings/world.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/settings/world.mjs b/module/settings/world.mjs index 7d44034..87e7872 100644 --- a/module/settings/world.mjs +++ b/module/settings/world.mjs @@ -11,7 +11,7 @@ export function registerWorldSettings() { type: Boolean, config: true, default: true, - requiresReload: true, + requiresReload: false, }); game.settings.register(__ID__, `globalAPI`, { From cfc5b9e70a3d20d386ff24c00a92942018924d31 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 29 May 2025 23:16:05 -0600 Subject: [PATCH 14/77] Add author info and links into the manifest file --- public/module.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/public/module.json b/public/module.json index ca51352..d4e09e1 100644 --- a/public/module.json +++ b/public/module.json @@ -7,6 +7,16 @@ "verified": 13, "minimum": 13 }, + "authors": [ + { + "name": "Oliver Akins", + "url": "https://github.com/Oliver-Akins" + } + ], + "url": "https://github.com/Oliver-Akins/Foundry-Stat-Tracker", + "manifest": "https://github.com/Oliver-Akins/Foundry-Stat-Tracker/releases/latest/download/module.json", + "download": "https://github.com/Oliver-Akins/Foundry-Stat-Tracker/releases/latest/download/release.zip", + "bugs": "https://github.com/Oliver-Akins/Foundry-Stat-Tracker/issues", "esmodules": [ "./module.mjs" ], From 2b782fd5ed41ab6133c5498382ce96c30bf73fbb Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 29 May 2025 23:35:04 -0600 Subject: [PATCH 15/77] Add issue templates for bug report and feature request --- .../workflows/ISSUE_TEMPLATE/BugReport.yaml | 53 +++++++++++++++++++ .../ISSUE_TEMPLATE/FeatureRequest.yaml | 11 ++++ 2 files changed, 64 insertions(+) create mode 100644 github/workflows/ISSUE_TEMPLATE/BugReport.yaml create mode 100644 github/workflows/ISSUE_TEMPLATE/FeatureRequest.yaml diff --git a/github/workflows/ISSUE_TEMPLATE/BugReport.yaml b/github/workflows/ISSUE_TEMPLATE/BugReport.yaml new file mode 100644 index 0000000..4e77681 --- /dev/null +++ b/github/workflows/ISSUE_TEMPLATE/BugReport.yaml @@ -0,0 +1,53 @@ +name: Bug Report +description: Have a bug to report, use this template! +labels: + - Bug +body: + - type: input + id: affected-version + attributes: + label: Affected Versions + description: >- + What version(s) of the stat-tracker module are you seeing this bug + happen on? If you put "latest" your bug report will be invalidated. + validations: + required: true + - type: input + id: system-id + attributes: + label: Game System + description: >- + What game system are you using the module with when you encounter the + bug? + placeholder: dnd5e + validations: + required: true + - type: textarea + id: description + attributes: + label: Bug Description + description: >- + Describe the bug you're encountering, the more detail you provide the + better. + validations: + required: true + - type: checkboxes + id: affected-environments + attributes: + label: Affected Environments + description: How are you accessing Foundry when you encounter this bug? + options: + - label: Desktop App + required: false + - label: Chrome + required: false + - label: Firefox + required: false + - type: textarea + id: support-summary + attributes: + label: Support Data + description: >- + Please open the settings tab in Foundry's sidebar, click "Support & + Issues", then press "Copy Report to Clipboard" and then paste that text + into the box below diff --git a/github/workflows/ISSUE_TEMPLATE/FeatureRequest.yaml b/github/workflows/ISSUE_TEMPLATE/FeatureRequest.yaml new file mode 100644 index 0000000..86caba4 --- /dev/null +++ b/github/workflows/ISSUE_TEMPLATE/FeatureRequest.yaml @@ -0,0 +1,11 @@ +name: Feature Request +description: Have a feature request? Use this template! +labels: + - Feature +body: + - type: textarea + attributes: + label: Description + description: A clear and concise description of the problem or missing capability + validations: + required: true \ No newline at end of file From a13310aaad1247b98cbcc8eef2503089cc3a4b9a Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 29 May 2025 23:42:14 -0600 Subject: [PATCH 16/77] Allow each database to determine if a user is allowed to perform CRUD operations on tables --- module/Apps/StatSidebar.mjs | 5 ++-- module/utils/databases/Database.mjs | 37 +++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/module/Apps/StatSidebar.mjs b/module/Apps/StatSidebar.mjs index 0360b6c..0542ca7 100644 --- a/module/Apps/StatSidebar.mjs +++ b/module/Apps/StatSidebar.mjs @@ -44,8 +44,10 @@ export class StatSidebar extends HandlebarsApplicationMixin(AbstractSidebarTab) // manageData: { label: `Manage Data`, action: `` }, }; - if (!game.user.isGM) { + if (!db.canCreateTables()) { delete controls.createTable; + }; + if (!db.canEditTables()) { delete controls.manageTables; }; @@ -55,7 +57,6 @@ export class StatSidebar extends HandlebarsApplicationMixin(AbstractSidebarTab) // delete controls.manageData; // }; - Hooks.callAll(`${__ID__}.getStatsSidebarControls`, controls); ctx.controls = Object.values(controls); return ctx; diff --git a/module/utils/databases/Database.mjs b/module/utils/databases/Database.mjs index 65aa285..7d7a592 100644 --- a/module/utils/databases/Database.mjs +++ b/module/utils/databases/Database.mjs @@ -37,9 +37,37 @@ const { deleteProperty, diffObject, expandObject, mergeObject } = foundry.utils; * consistency across databases. */ export class Database { + // MARK: Permissions + /** + * Indicates whether the authenticated user has permission and is able to + * create tables with the specific database implementation. (because tables + * are stored as world settings by default, this checks if the user is a GM) + */ + static canCreateTables() { + return game.user.isGM; + }; + + /** + * Indicates whether the authenticated user has permission and is able to + * edit tables with the specific database implementation. (because tables + * are stored as world settings by default, this checks if the user is a GM) + */ + static canEditTables() { + return game.user.isGM; + }; + + /** + * Indicates whether the authenticated user has permission and is able to + * delete tables with the specific database implementation. (because tables + * are stored as world settings by default, this checks if the user is a GM) + */ + static canDeleteTables() { + return game.user.isGM; + }; + // MARK: Table Ops static async createTable(tableConfig) { - if (!game.user.isGM) { + if (!this.canCreateTables()) { ui.notifications.error(`You do not have the required permission to create a new table`); return false; }; @@ -98,6 +126,11 @@ export class Database { }; static async updateTable(tableID, changes) { + if (!this.canEditTables()) { + ui.notifications.error(`You don't have the required permission to edit tables`); + return false; + }; + const table = this.getTable(tableID); if (!tables[tableID]) { ui.notifications.error(`Cannot update table that doesn't exist`); @@ -136,7 +169,7 @@ export class Database { }; static async deleteTable(tableID) { - if (!game.user.isGM) { + if (!this.canDeleteTables()) { ui.notifications.error(`You do not have the required permission to delete a table`); return false; }; From 5c1a33292153d4fcf60ab83c8f84b213caafd0d0 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 29 May 2025 23:46:58 -0600 Subject: [PATCH 17/77] Keep writing initial documentation --- packs/docs/_source/English_pBOyeBDuTeowuDOE.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json index 18863de..85dec66 100644 --- a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json +++ b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json @@ -16,7 +16,7 @@ "image": {}, "text": { "format": 1, - "content": "

Thank you for installing Stat Tracker!

This module aims to provide a clean way of keeping track of any data points you want within Foundry, whether that be dice rolls, or other things like how many natural 1s to natural 20s you get.

I was inspired by the dicestats module, however it only allows tracking dice statistics, which is something I found myself needing to work around, so I decided to make this module to fill that gap.

You've already done the hardest part of set up for this module to work on the basic level, installing it! However if you want a more advanced set up for the module, this Journal is your go-to for information.

This module makes use of multiple key terms which are important for your understanding, the main terms you need to understand are:

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{Row}

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{Table}

  • and, @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{Bucket}

You can find information about what each of those terms mean within the \"Key Concepts\" section of the documentation or by clicking the above links.

" + "content": "

Thank you for installing Stat Tracker!

This module aims to provide a clean way of keeping track of any data points you want within Foundry, whether that be dice rolls, or other things like how many natural 1s to natural 20s you get.

I was inspired by the dicestats module, however it only allows tracking dice statistics, which is something I found myself needing to work around and struggle against, so I decided to make this module to fill that gap while improving upon the graph rendering.

You've already done the hardest part of set up for this module to work on the basic level, installing it! However if you want a more advanced set up for the module, this Journal is your go-to for information.

This module makes use of multiple key terms which are important for your understanding, the main terms you need to understand are:

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{Row}

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{Table}

  • and, @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{Bucket}

You can find information about what each of those terms mean within the \"Key Concepts\" section of the documentation or by clicking the above links.

Dice Stats

If you just want the module to track dice stats, then it's probably already done for you! All you need to do is start playing your game.

The only situation in which you need to do additional configuration for your game is if you are playing a game which doesn't use the standard dice sizes (d4, d6, d8, d10, d12, d20, d100), however you can add any dice size you want by creating a new table named Dice/dX where X is the number of sides the dice should have (e.g. for a 3-sided dice you would make the table name be Dice/d3). By adding a table in this way, it's configuration will be locked for editing and the only way to change it will be to delete the table entirely.

System Support

Currently this module does not support system-specific implementations, however in the future I am planning on adding support for systems as desired by users (the dnd5e system will be the first one supported once I can).

Feature Requests and Bugs

All feature requests and bug reports can go the module's GitHub page : https://github.com/Oliver-Akins/Foundry-Stat-Tracker/issues

" }, "video": { "controls": true, @@ -36,7 +36,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748328891633, - "modifiedTime": 1748406648943, + "modifiedTime": 1748583825340, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.xcR48pakEm49hbc2" @@ -231,7 +231,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748330762378, - "modifiedTime": 1748408480001, + "modifiedTime": 1748582414432, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.UuAWTQtd3QMKOWvU" @@ -367,7 +367,7 @@ "image": {}, "text": { "format": 1, - "content": "" + "content": "

Documentation Coming Soon

" }, "video": { "controls": true, @@ -387,7 +387,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748393919924, - "modifiedTime": 1748394635911, + "modifiedTime": 1748583846540, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.Z31C8BPl4ZXDn3R6" From 46400ca318efa3bc68e23992e2063131c64626b8 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 21:27:23 -0600 Subject: [PATCH 18/77] Make the migrationCheck methods synchronous --- module/utils/databases/Database.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/utils/databases/Database.mjs b/module/utils/databases/Database.mjs index 7d7a592..1a55f62 100644 --- a/module/utils/databases/Database.mjs +++ b/module/utils/databases/Database.mjs @@ -261,7 +261,7 @@ export class Database { * * @returns {boolean} */ - static async canPerformMigration() { + static canPerformMigration() { // TODO: this *must* account for isActiveGM, because otherwise the // world setting cannot be updated after the migration finishes. return game.user.isActiveGM; @@ -274,7 +274,7 @@ export class Database { * @param {string} lastVersion The version that was last active * @returns {boolean} */ - static async requiresMigrationFrom(lastVersion) { + static requiresMigrationFrom(lastVersion) { return foundry.utils.isNewerVersion(__VERSION__, lastVersion); }; From c7b9146e34faa9e049adc9d01a4a89cd6cfbc07a Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 21:27:55 -0600 Subject: [PATCH 19/77] Remove logs that aren't required --- module/Apps/StatsViewer.mjs | 3 --- 1 file changed, 3 deletions(-) diff --git a/module/Apps/StatsViewer.mjs b/module/Apps/StatsViewer.mjs index 14c61cd..dbe6844 100644 --- a/module/Apps/StatsViewer.mjs +++ b/module/Apps/StatsViewer.mjs @@ -213,8 +213,6 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { this._privacySetting, ); - Logger.log(userData); - const data = {}; const allBuckets = new Set(); @@ -281,7 +279,6 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { }), }, }; - console.log(`graphData`, this._graphData); }; // #endregion Data Prep From de35935a2d1d6fface111e615b89562d63c3010c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 21:28:15 -0600 Subject: [PATCH 20/77] Disable legend clicks in the graphs (closes #4) --- module/Apps/StatsViewer.mjs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/module/Apps/StatsViewer.mjs b/module/Apps/StatsViewer.mjs index dbe6844..57a1cd8 100644 --- a/module/Apps/StatsViewer.mjs +++ b/module/Apps/StatsViewer.mjs @@ -264,6 +264,11 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { stacked: table.graph?.stacked ?? false, }, }, + plugins: { + legend: { + onClick: null, + }, + }, }, data: { labels: sortedBucketNames, From b4b3becec02fdf8c8c2f8bf24fe621bb56f19398 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 23:24:51 -0600 Subject: [PATCH 21/77] Implement the deleteTable method on the MemoryDatabase --- module/utils/databases/Memory.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/module/utils/databases/Memory.mjs b/module/utils/databases/Memory.mjs index 6540ba7..8dba510 100644 --- a/module/utils/databases/Memory.mjs +++ b/module/utils/databases/Memory.mjs @@ -117,6 +117,10 @@ export class MemoryDatabase extends Database { return true; }; + static async deleteTable(tableID) { + return delete this.#tables[tableID]; + }; + static async createRow(table, userID, row, { rerender = true } = {}) { if (!this.#tables[table]) { return }; this.#rows[userID] ??= {}; From a7c11acc2f1e5720725d2e7d7dcb3f3da5885709 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 23:25:34 -0600 Subject: [PATCH 22/77] Add an action to delete a table (closes #21) --- module/Apps/TableManager.mjs | 39 ++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/module/Apps/TableManager.mjs b/module/Apps/TableManager.mjs index 7516d51..2bd2e82 100644 --- a/module/Apps/TableManager.mjs +++ b/module/Apps/TableManager.mjs @@ -4,7 +4,7 @@ import { filePath } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; import { smallToLarge } from "../utils/sorters/smallToLarge.mjs"; -const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; +const { HandlebarsApplicationMixin, ApplicationV2, DialogV2 } = foundry.applications.api; const { isEmpty } = foundry.utils; export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { @@ -24,6 +24,12 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { contentClasses: [`st-scrollable`], controls: [ // Add action for deleting the table + { + icon: `fa-solid fa-trash`, + label: `Delete Selected Table`, + action: `deleteTable`, + visible: () => CONFIG.stats.db.canDeleteTables(), + }, ], }, position: { @@ -35,7 +41,9 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { closeOnSubmit: false, handler: this.#submit, }, - actions: {}, + actions: { + deleteTable: this.#deleteTable, + }, }; static PARTS = { @@ -239,5 +247,32 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { } await CONFIG.stats.db.updateTable(this.activeTableID, formData.object); }; + + /** + * @this {TableManager} + */ + static async #deleteTable() { + const table = await CONFIG.stats.db.getTable(this.activeTableID); + Logger.debug({ table }); + if (!table) { + ui.notifications.error( + `You must select a table before you can delete it`, + { console: false }, + ); + return; + }; + + const confirmed = await DialogV2.confirm({ + window: { + title: `Confirm Deletion`, + }, + content: `

Are you sure you want to delete the table: ${this.activeTableID}

`, + }); + if (!confirmed) { return }; + + CONFIG.stats.db.deleteTable(this.activeTableID); + this._selectedTable = null; + this.render(); + }; // #endregion Actions }; From 66edecc217ee8e9dd295b75931eacaa5515bcc67 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 23:26:29 -0600 Subject: [PATCH 23/77] Finish the majority of the docs that I feel are necessary for v1.0.0 --- packs/docs/_source/English_pBOyeBDuTeowuDOE.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json index 85dec66..1c363da 100644 --- a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json +++ b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json @@ -55,7 +55,7 @@ "image": {}, "text": { "format": 1, - "content": "

Tables are quite close to being a \"container\" for every piece of data within the module, every @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{row} within the module must be associated with a particular table. The table is responsible for maintaining all of the @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{bucket} and @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.IXZpEBEJsvOpY3OL]{graph} configurations, controlling what data is allowed to be saved and how to represent that data.

You can manage almost all tables within the module by using the @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.Z31C8BPl4ZXDn3R6]{Table Manager}.

" + "content": "

Tables are quite close to being a \"container\" for every piece of data within the module, every @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{row} within the module must be associated with a particular table. The table is responsible for maintaining all of the @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{bucket} and @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.IXZpEBEJsvOpY3OL]{graph} configurations, controlling what data is allowed to be saved and how to represent that data.

Subtables

Subtables are a concept used to group multiple tables together in a logical way, taking the Dice/d10 table as an example, the \"table ID\" is the full \"Dice/d10\", while the table name is \"Dice\" and the subtable name is \"d10\". This allows the module to group all of the \"Dice\" tables together in the user interfaces.

Limitations

  • Subtables can only go one level deep (e.g. Table/SubTable/SubSubTable is an invalid table ID and the module will reject it)

  • Subtables within the \"Dice\" table cannot be edited, they are required to use a @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO#range-buckets]{Range Bucket} and the settings are locked. This is because the module makes certain assumptions about the subtables within that space adhering to those requirements and making changes to them causes some issues. If you want to make a table that doesn't conform to those requirements, you can make a new table for your own purposes though!

" }, "video": { "controls": true, @@ -75,7 +75,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748329356680, - "modifiedTime": 1748394635911, + "modifiedTime": 1748658060472, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.ugzCCxQskUSYMZR4" @@ -211,7 +211,7 @@ "image": {}, "text": { "format": 1, - "content": "

The module provides a bunch of settings to be able to control how it interacts with Foundry in various ways. Each setting has a description provided in the settings configuration window, but these descriptions will go more in depth than the ones in there.

Roll Auto-Tracking

This tells the module to automatically track rolls that are sent to the chat, this includes systems, modules, and other rolls like RollTables. As long as it gets sent to the chat, this will allow that roll to automatically be tracked.

By default, this only tracks the standard dice sizes (d4, d6, d8, d10, d12, d20, d100), however you can add any dice size you want by creating a new table named Dice/dX where X is the number of sides the dice should have (e.g. for a 3-sided dice you would make the table name be Dice/d3). By adding a table in this way, it's configuration will be locked for editing and the only way to change it will be to delete the table entirely.

For most systems you will want to leave this setting enabled, because otherwise there is a chance that no dice rolls will be tracked at all unless the system has specifically implemented an integration with the module.

Global API

This setting is primarily targeted at users who would like to integrate stats tracking into macros, as it exposes a globally available stats variable with references to all of the exposed methods and utility helpers of the module. This can sometimes cause conflicts with systems or other modules, so make sure that there isn't already another a global variable named stats before enabling this setting.

Below is an example of how to retrieve the module's API both with and without this setting enabled:

// with it enabled:\nconst statViewer = new stats.Apps.StatsViewer;\nstatViewer.render({ force: true });\n\n// with it disabled\nconst api = game.modules.get(`stat-tracker`)?.api;\nconst statViewer = new api.Apps.StatsViewer;\nstatViewer?.render({ force: true });

Stats Sidebar Tab

" + "content": "

The module provides a bunch of settings to be able to control how it interacts with Foundry in various ways. Each setting has a description provided in the settings configuration window, but these descriptions will go more in depth than the ones in there. This will not include all settings, just the ones that would be more beneficial to have additional clarification for.

Roll Auto-Tracking

This tells the module to automatically track rolls that are sent to the chat, this includes systems, modules, and other rolls like RollTables. As long as it gets sent to the chat, this will allow that roll to automatically be tracked.

By default, this only tracks the standard dice sizes (d4, d6, d8, d10, d12, d20, d100), however you can add any dice size you want by creating a new table named Dice/dX where X is the number of sides the dice should have (e.g. for a 3-sided dice you would make the table name be Dice/d3). By adding a table in this way, it's configuration will be locked for editing and the only way to change it will be to delete the table entirely.

For most systems you will want to leave this setting enabled, because otherwise there is a chance that no dice rolls will be tracked at all unless the system has specifically implemented an integration with the module.

Global API

This setting is primarily targeted at users who would like to integrate stats tracking into macros, as it exposes a globally available stats variable with references to all of the exposed methods and utility helpers of the module. This can sometimes cause conflicts with systems or other modules, so make sure that there isn't already another a global variable named stats before enabling this setting.

Below is an example of how to retrieve the module's API both with and without this setting enabled:

// with it enabled:\nconst statViewer = new stats.Apps.StatsViewer;\nstatViewer.render({ force: true });\n\n// with it disabled\nconst api = game.modules.get(`stat-tracker`)?.api;\nconst statViewer = new api.Apps.StatsViewer;\nstatViewer?.render({ force: true });
" }, "video": { "controls": true, @@ -231,7 +231,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748330762378, - "modifiedTime": 1748582414432, + "modifiedTime": 1748658920308, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.UuAWTQtd3QMKOWvU" @@ -406,7 +406,7 @@ "image": {}, "text": { "format": 1, - "content": "

Privacy Modes

" + "content": "

All of these enums are available within <api>.enums, they are read-only and cannot be modified by other plugins.

Privacy Modes

This enum is used by the module to specify all of the privacy levels that it uses.

The valid values are:

  • GM - Representing that only gamemasters and assistant gamemasters will be able to see this data entry. This mode is similar to the \"Blind GM Roll\" roll mode.

  • PRIVATE - Indicating that this is a piece of private data, that only gamemasters, assistant gamemasters, and the user who owns the piece of data will be able to see it.

  • SELF - Similar to the \"GM\" level, but instead of gamemasters, it's only the user who owns the piece of data that's able to see it.

  • PUBLIC - Everyone can see it.

" }, "video": { "controls": true, @@ -426,7 +426,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748394110971, - "modifiedTime": 1748394637940, + "modifiedTime": 1748657380240, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.WYaZPgSRDx8L7Zmq" @@ -484,7 +484,7 @@ "image": {}, "text": { "format": 1, - "content": "

This is the interface which all @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.PcdmuLgNM15h0in1]{Database Adapters} must conform to in order for the module to function. If they do not conform to this a warning will be thrown and the module will override the provided database with a database adapter which does nothing, so that the existing data will be protected from errors.

" + "content": "

This is the interface which all @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.PcdmuLgNM15h0in1]{Database Adapters} must conform to in order for the module to function. If they do not conform to this a warning will be thrown and the module will override the provided database with a database adapter which does nothing, so that the existing data will be protected from errors.

The best way to learn about the required database interface is to read the implementation of the interface that all database adapters are required to extend.

" }, "video": { "controls": true, @@ -504,7 +504,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748408916163, - "modifiedTime": 1748409672979, + "modifiedTime": 1748656904440, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.PlKHrrb61Uc1sGbN" From 9e037818a688bac93b4b13f3a71a38404f08b73c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 23:50:59 -0600 Subject: [PATCH 24/77] Finish creating the build step for prod to handle the compendia pack(s) --- scripts/buildCompendia.mjs | 8 ++++++-- scripts/extractCompendia.mjs | 7 +++++-- vite.config.js | 31 +++++++++++++++++++++++++++---- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/scripts/buildCompendia.mjs b/scripts/buildCompendia.mjs index ea0cae0..8ce251c 100644 --- a/scripts/buildCompendia.mjs +++ b/scripts/buildCompendia.mjs @@ -2,14 +2,16 @@ import { existsSync } from "fs"; import { readFile } from "fs/promises"; import { join } from "path"; import { compilePack } from "@foundryvtt/foundryvtt-cli"; +import { pathToFileURL } from "url"; -async function main() { +export async function buildCompendia() { const manifest = JSON.parse(await readFile(`./public/module.json`, `utf-8`)); if (!manifest.packs || manifest.packs.length === 0) { console.log(`No compendium packs defined`); process.exit(0); }; + console.log(`Packing compendia`); for (const compendium of manifest.packs) { console.debug(`Packing ${compendium.label} (${compendium.name})`); @@ -29,4 +31,6 @@ async function main() { console.log(`Finished packing compendia`) }; -main(); +if (import.meta.url === pathToFileURL(process.argv[1]).href) { + buildCompendia(); +}; diff --git a/scripts/extractCompendia.mjs b/scripts/extractCompendia.mjs index 939c31d..df3b6a6 100644 --- a/scripts/extractCompendia.mjs +++ b/scripts/extractCompendia.mjs @@ -2,13 +2,14 @@ import { readFile } from "fs/promises"; import { join } from "path"; import { extractPack } from "@foundryvtt/foundryvtt-cli"; -async function main() { +export async function extractCompendia() { const manifest = JSON.parse(await readFile(`./public/module.json`, `utf-8`)); if (!manifest.packs || manifest.packs.length === 0) { console.log(`No compendium packs defined`); process.exit(0); }; + console.log(`Extracting compendia`); for (const compendium of manifest.packs) { console.debug(`Unpacking ${compendium.label} (${compendium.name})`); @@ -24,4 +25,6 @@ async function main() { console.log(`Finished unpacking compendia`); }; -main(); +if (import.meta.url === pathToFileURL(process.argv[1]).href) { + extractCompendia(); +}; diff --git a/vite.config.js b/vite.config.js index d849e07..5bdc955 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,6 +1,8 @@ /* eslint-disable no-undef */ +import { cp, rm } from "fs/promises"; import { readFileSync, symlinkSync } from "fs"; +import { buildCompendia } from "./scripts/buildCompendia.mjs"; import { defineConfig } from "vite"; import { glob } from "glob"; import path from "path"; @@ -46,10 +48,9 @@ The intent of this plugin is to handle the symlinking of the compendium packs so that they can modified during dev without needing to worry about the rebuild destroying the in-progress compendia data. */ -function handlePacks() { +function symlinkPacks() { return { writeBundle(options) { - console.log(options.dir); symlinkSync( path.resolve(__dirname, `packs`), `${options.dir}/packs`, @@ -59,15 +60,37 @@ function handlePacks() { }; }; +/* +The intent of this plugin is to handle the copying, cleaning and compiling of +compendia packs for production +*/ +function buildPacks() { + return { + async writeBundle(options) { + // console.log(options); + const buildDir = options.dir; + await buildCompendia(); + await cp(`${__dirname}/packs`, `${buildDir}/packs`, { recursive: true, force: true }); + for (const file of glob.sync(`${buildDir}/packs/**/_source/`)) { + await rm(file, { recursive: true, force: true }); + }; + }, + }; +}; + // MARK: config export default defineConfig(({ mode }) => { const isProd = mode === `prod`; const plugins = []; - if (!isProd) { + if (isProd) { plugins.push( - handlePacks(), + buildPacks(), + ); + } else { + plugins.push( + symlinkPacks(), watcher( `./public`, ), From 8e667bf3ace346a1278197476d65d1fa6a5d8fe8 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 23:53:00 -0600 Subject: [PATCH 25/77] Move the issue templates into the correct location so that they actually work --- github/{workflows => }/ISSUE_TEMPLATE/BugReport.yaml | 0 github/{workflows => }/ISSUE_TEMPLATE/FeatureRequest.yaml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename github/{workflows => }/ISSUE_TEMPLATE/BugReport.yaml (100%) rename github/{workflows => }/ISSUE_TEMPLATE/FeatureRequest.yaml (100%) diff --git a/github/workflows/ISSUE_TEMPLATE/BugReport.yaml b/github/ISSUE_TEMPLATE/BugReport.yaml similarity index 100% rename from github/workflows/ISSUE_TEMPLATE/BugReport.yaml rename to github/ISSUE_TEMPLATE/BugReport.yaml diff --git a/github/workflows/ISSUE_TEMPLATE/FeatureRequest.yaml b/github/ISSUE_TEMPLATE/FeatureRequest.yaml similarity index 100% rename from github/workflows/ISSUE_TEMPLATE/FeatureRequest.yaml rename to github/ISSUE_TEMPLATE/FeatureRequest.yaml From 1bcd01ed75106267e47fda9894877be0ac37c877 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 23:55:13 -0600 Subject: [PATCH 26/77] Rename github folder to .github --- {github => .github}/ISSUE_TEMPLATE/BugReport.yaml | 0 {github => .github}/ISSUE_TEMPLATE/FeatureRequest.yaml | 0 {github => .github}/workflows/draft-release.yaml | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {github => .github}/ISSUE_TEMPLATE/BugReport.yaml (100%) rename {github => .github}/ISSUE_TEMPLATE/FeatureRequest.yaml (100%) rename {github => .github}/workflows/draft-release.yaml (100%) diff --git a/github/ISSUE_TEMPLATE/BugReport.yaml b/.github/ISSUE_TEMPLATE/BugReport.yaml similarity index 100% rename from github/ISSUE_TEMPLATE/BugReport.yaml rename to .github/ISSUE_TEMPLATE/BugReport.yaml diff --git a/github/ISSUE_TEMPLATE/FeatureRequest.yaml b/.github/ISSUE_TEMPLATE/FeatureRequest.yaml similarity index 100% rename from github/ISSUE_TEMPLATE/FeatureRequest.yaml rename to .github/ISSUE_TEMPLATE/FeatureRequest.yaml diff --git a/github/workflows/draft-release.yaml b/.github/workflows/draft-release.yaml similarity index 100% rename from github/workflows/draft-release.yaml rename to .github/workflows/draft-release.yaml From 777ea69b66e13de5b7216d9203e84a37da280794 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 23:58:50 -0600 Subject: [PATCH 27/77] Update manifest version and bugs link --- packs/docs/_source/English_pBOyeBDuTeowuDOE.json | 2 +- public/module.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json index 1c363da..cba21ab 100644 --- a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json +++ b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json @@ -16,7 +16,7 @@ "image": {}, "text": { "format": 1, - "content": "

Thank you for installing Stat Tracker!

This module aims to provide a clean way of keeping track of any data points you want within Foundry, whether that be dice rolls, or other things like how many natural 1s to natural 20s you get.

I was inspired by the dicestats module, however it only allows tracking dice statistics, which is something I found myself needing to work around and struggle against, so I decided to make this module to fill that gap while improving upon the graph rendering.

You've already done the hardest part of set up for this module to work on the basic level, installing it! However if you want a more advanced set up for the module, this Journal is your go-to for information.

This module makes use of multiple key terms which are important for your understanding, the main terms you need to understand are:

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{Row}

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{Table}

  • and, @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{Bucket}

You can find information about what each of those terms mean within the \"Key Concepts\" section of the documentation or by clicking the above links.

Dice Stats

If you just want the module to track dice stats, then it's probably already done for you! All you need to do is start playing your game.

The only situation in which you need to do additional configuration for your game is if you are playing a game which doesn't use the standard dice sizes (d4, d6, d8, d10, d12, d20, d100), however you can add any dice size you want by creating a new table named Dice/dX where X is the number of sides the dice should have (e.g. for a 3-sided dice you would make the table name be Dice/d3). By adding a table in this way, it's configuration will be locked for editing and the only way to change it will be to delete the table entirely.

System Support

Currently this module does not support system-specific implementations, however in the future I am planning on adding support for systems as desired by users (the dnd5e system will be the first one supported once I can).

Feature Requests and Bugs

All feature requests and bug reports can go the module's GitHub page : https://github.com/Oliver-Akins/Foundry-Stat-Tracker/issues

" + "content": "

Thank you for installing Stat Tracker!

This module aims to provide a clean way of keeping track of any data points you want within Foundry, whether that be dice rolls, or other things like how many natural 1s to natural 20s you get.

I was inspired by the dicestats module, however it only allows tracking dice statistics, which is something I found myself needing to work around and struggle against, so I decided to make this module to fill that gap while improving upon the graph rendering.

You've already done the hardest part of set up for this module to work on the basic level, installing it! However if you want a more advanced set up for the module, this Journal is your go-to for information.

This module makes use of multiple key terms which are important for your understanding, the main terms you need to understand are:

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.S7Z6mZ0JablJVQJu]{Row}

  • @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{Table}

  • and, @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO]{Bucket}

You can find information about what each of those terms mean within the \"Key Concepts\" section of the documentation or by clicking the above links.

Dice Stats

If you just want the module to track dice stats, then it's probably already done for you! All you need to do is start playing your game.

The only situation in which you need to do additional configuration for your game is if you are playing a game which doesn't use the standard dice sizes (d4, d6, d8, d10, d12, d20, d100), however you can add any dice size you want by creating a new table named Dice/dX where X is the number of sides the dice should have (e.g. for a 3-sided dice you would make the table name be Dice/d3). By adding a table in this way, it's configuration will be locked for editing and the only way to change it will be to delete the table entirely.

System Support

Currently this module does not support system-specific implementations, however in the future I am planning on adding support for systems as desired by users (the dnd5e system will be the first one supported once I can).

Feature Requests and Bugs

All feature requests and bug reports can go the module's GitHub page : https://github.com/Oliver-Akins/Foundry-Stat-Tracker/issues/new/choose

" }, "video": { "controls": true, diff --git a/public/module.json b/public/module.json index d4e09e1..50eecbf 100644 --- a/public/module.json +++ b/public/module.json @@ -1,7 +1,7 @@ { "id": "stat-tracker", "title": "Stats Tracker", - "version": "0.0.1", + "version": "1.0.0", "compatibility": { "maximum": 13, "verified": 13, @@ -16,7 +16,7 @@ "url": "https://github.com/Oliver-Akins/Foundry-Stat-Tracker", "manifest": "https://github.com/Oliver-Akins/Foundry-Stat-Tracker/releases/latest/download/module.json", "download": "https://github.com/Oliver-Akins/Foundry-Stat-Tracker/releases/latest/download/release.zip", - "bugs": "https://github.com/Oliver-Akins/Foundry-Stat-Tracker/issues", + "bugs": "https://github.com/Oliver-Akins/Foundry-Stat-Tracker/issues/new/choose", "esmodules": [ "./module.mjs" ], From 7b413437783e4349a9f15ae3cf2e8ff95fbd98ea Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Fri, 30 May 2025 23:59:14 -0600 Subject: [PATCH 28/77] Improve logs in the compendia helpers --- scripts/buildCompendia.mjs | 4 ++-- scripts/extractCompendia.mjs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/buildCompendia.mjs b/scripts/buildCompendia.mjs index 8ce251c..a782913 100644 --- a/scripts/buildCompendia.mjs +++ b/scripts/buildCompendia.mjs @@ -25,10 +25,10 @@ export async function buildCompendia() { join(process.cwd(), compendium.path), { recursive: true }, ); - console.debug(`Finished packing ${compendium.name}`); + console.debug(`Finished packing compendium: ${compendium.name}`); }; - console.log(`Finished packing compendia`) + console.log(`Finished packing all compendia`) }; if (import.meta.url === pathToFileURL(process.argv[1]).href) { diff --git a/scripts/extractCompendia.mjs b/scripts/extractCompendia.mjs index df3b6a6..84dd87d 100644 --- a/scripts/extractCompendia.mjs +++ b/scripts/extractCompendia.mjs @@ -19,10 +19,10 @@ export async function extractCompendia() { src, { recursive: true }, ); - console.debug(`Finished packing ${compendium.name}`); + console.debug(`Finished unpacking compendium: ${compendium.name}`); }; - console.log(`Finished unpacking compendia`); + console.log(`Finished unpacking all compendia`); }; if (import.meta.url === pathToFileURL(process.argv[1]).href) { From b70e4107b7be10da2a4be720e0826059a6af6101 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 00:14:08 -0600 Subject: [PATCH 29/77] Add a license --- LICENSE | 373 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 373 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dbdb0fa --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. \ No newline at end of file From d28f4af088e287ca9487ed4ae8dee8ef3f4d9532 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 00:14:30 -0600 Subject: [PATCH 30/77] Add a build step to copy the license and readme into the build output. --- vite.config.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/vite.config.js b/vite.config.js index 5bdc955..ddfd88d 100644 --- a/vite.config.js +++ b/vite.config.js @@ -67,7 +67,6 @@ compendia packs for production function buildPacks() { return { async writeBundle(options) { - // console.log(options); const buildDir = options.dir; await buildCompendia(); await cp(`${__dirname}/packs`, `${buildDir}/packs`, { recursive: true, force: true }); @@ -78,11 +77,27 @@ function buildPacks() { }; }; +/* +Allows copying a file from somewhere into the build directory once the build has +completed. +*/ +function copyFile(filepath, targetPath) { + return { + async writeBundle(options) { + const buildDir = options.dir; + await cp(filepath, `${buildDir}/${targetPath}`); + }, + }; +}; + // MARK: config export default defineConfig(({ mode }) => { const isProd = mode === `prod`; - const plugins = []; + const plugins = [ + copyFile(`LICENSE`, `LICENSE`), + copyFile(`README.md`, `README.md`), + ]; if (isProd) { plugins.push( From cf800f0a51021aba0a1180d95de630e09062ebe2 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 00:17:45 -0600 Subject: [PATCH 31/77] Update the github action to make it work with the prod.dist folder better --- .github/workflows/draft-release.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/draft-release.yaml b/.github/workflows/draft-release.yaml index b3f9b77..7553427 100644 --- a/.github/workflows/draft-release.yaml +++ b/.github/workflows/draft-release.yaml @@ -39,7 +39,7 @@ jobs: run: cat prod.dist/module.temp.json | jq -r --tab '.download = "https://github.com/${{ github.repository }}/releases/download/v${{ steps.version.outputs.version }}/release.zip"' > prod.dist/module.json - name: Create the zip - run: zip -r release.zip prod.dist/* + run: cd prod.dist; zip -r release.zip * - name: Create the draft release uses: ncipollo/release-action@v1 @@ -48,4 +48,4 @@ jobs: commit: ${{ github.ref }} draft: true generateReleaseNotes: true - artifacts: "release.zip,prod.dist/module.json" \ No newline at end of file + artifacts: "prod.dist/release.zip,prod.dist/module.json" From 033be40e8076d1a85dfd0c16ab771dce68f2f51e Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 00:22:50 -0600 Subject: [PATCH 32/77] Read the correct manifest file --- .github/workflows/draft-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-release.yaml b/.github/workflows/draft-release.yaml index 7553427..29d3d63 100644 --- a/.github/workflows/draft-release.yaml +++ b/.github/workflows/draft-release.yaml @@ -18,7 +18,7 @@ jobs: - name: Reading the system.json for the version id: "version" - run: cat prod.dist/system.json | echo version=`jq -r ".version"` >> "$GITHUB_OUTPUT" + run: cat prod.dist/module.json | echo version=`jq -r ".version"` >> "$GITHUB_OUTPUT" # Check that tag doesn't exist - uses: mukunku/tag-exists-action@v1.5.0 From d888a0c9bec1f03816fe8a4d49c9c153cb08dbe4 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 00:28:12 -0600 Subject: [PATCH 33/77] Have the build delete all of the temp files --- .github/workflows/draft-release.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/draft-release.yaml b/.github/workflows/draft-release.yaml index 29d3d63..de3b830 100644 --- a/.github/workflows/draft-release.yaml +++ b/.github/workflows/draft-release.yaml @@ -38,6 +38,9 @@ jobs: id: manifest-update run: cat prod.dist/module.temp.json | jq -r --tab '.download = "https://github.com/${{ github.repository }}/releases/download/v${{ steps.version.outputs.version }}/release.zip"' > prod.dist/module.json + - name: Delete temp files from build + run: rm prod.dist/*.temp.* + - name: Create the zip run: cd prod.dist; zip -r release.zip * From c23a656574a88d802bf5d0907575990d8aebee2d Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 00:50:20 -0600 Subject: [PATCH 34/77] Make it so that I can test the prod build locally easier --- package.json | 4 ++-- vite.config.js | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 2584d72..441ebfc 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "lint:nofix": "eslint", "dev": "NODE_ENV=development vite build --mode dev --watch", "dev:once": "NODE_ENV=development vite build --mode dev", - "staging": "NODE_ENV=staging vite build --mode dev --watch", - "staging:once": "NODE_ENV=staging vite build --mode dev", + "staging": "NODE_ENV=staging vite build --mode staging --watch", + "staging:once": "NODE_ENV=staging vite build --mode staging", "build": "NODE_ENV=production vite build --mode prod" }, "devDependencies": { diff --git a/vite.config.js b/vite.config.js index ddfd88d..b6ef53f 100644 --- a/vite.config.js +++ b/vite.config.js @@ -92,7 +92,11 @@ function copyFile(filepath, targetPath) { // MARK: config export default defineConfig(({ mode }) => { - const isProd = mode === `prod`; + const isProd = [`prod`, `staging`].includes(mode); + let outMode = mode; + if (mode === `staging`) { + outMode = `dev`; + }; const plugins = [ copyFile(`LICENSE`, `LICENSE`), @@ -125,6 +129,10 @@ export default defineConfig(({ mode }) => { mode: isProd ? `production` : `development`, build: { minify: isProd ? `terser` : false, + terserOptions: { + keep_classnames: true, + keep_fnames: true, + }, sourcemap: true, rollupOptions: { input: { @@ -136,7 +144,7 @@ export default defineConfig(({ mode }) => { format: `esm`, }, }, - outDir: `${mode}.dist`, + outDir: `${outMode}.dist`, emptyOutDir: true, }, }; From 79780e885b88256c457511ff9aafac3400ea0b18 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 10:05:00 -0600 Subject: [PATCH 35/77] Only copy the licence and readme on production builds --- vite.config.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/vite.config.js b/vite.config.js index b6ef53f..da6d7ca 100644 --- a/vite.config.js +++ b/vite.config.js @@ -98,13 +98,12 @@ export default defineConfig(({ mode }) => { outMode = `dev`; }; - const plugins = [ - copyFile(`LICENSE`, `LICENSE`), - copyFile(`README.md`, `README.md`), - ]; + const plugins = []; if (isProd) { plugins.push( + copyFile(`LICENSE`, `LICENSE`), + copyFile(`README.md`, `README.md`), buildPacks(), ); } else { From c7379a48f47076d5c414aec8a91e48155f19d241 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 10:05:08 -0600 Subject: [PATCH 36/77] Increment version number --- public/module.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/module.json b/public/module.json index 50eecbf..be69283 100644 --- a/public/module.json +++ b/public/module.json @@ -1,7 +1,7 @@ { "id": "stat-tracker", "title": "Stats Tracker", - "version": "1.0.0", + "version": "1.0.1", "compatibility": { "maximum": 13, "verified": 13, From 2c733385ef43d9b2bf4c109f211844e9b13c72f9 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 10:17:20 -0600 Subject: [PATCH 37/77] Close the TableCreator and dice namespace warning when the table is made successfully (closes #25) --- module/Apps/TableCreator.mjs | 40 ++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/module/Apps/TableCreator.mjs b/module/Apps/TableCreator.mjs index 5d9f511..4512ad3 100644 --- a/module/Apps/TableCreator.mjs +++ b/module/Apps/TableCreator.mjs @@ -109,6 +109,7 @@ export class TableCreator extends HandlebarsApplicationMixin(ApplicationV2) { this.render(); }; + /** @this {TableCreator} */ static async #createTable() { /** @type {string} */ const name = this._name; @@ -122,25 +123,38 @@ export class TableCreator extends HandlebarsApplicationMixin(ApplicationV2) { return; }; + let created = false; if (name.startsWith(`Dice`)) { if (!name.match(diceNamespacePattern)) { ui.notifications.error(`Table name doesn't conform to the "Dice/dX" format required by the Dice namespace.`); return; }; const size = Number(name.replace(`Dice/d`, ``)); - await CONFIG.stats.db.createTable(createDiceTable(size)); - return; - }; + created = await CONFIG.stats.db.createTable(createDiceTable(size)); + if (created) { + this.close(); + ui.notifications.remove(this.#diceNamespaceAlert); + this.#diceNamespaceAlert = null; + }; + } else { + created = await CONFIG.stats.db.createTable({ + name, + buckets: { + type: this._type, + }, + graph: { + type: `bar`, + stacked: true, + }, + }); + } - await CONFIG.stats.db.createTable({ - name, - buckets: { - type: this._type, - }, - graph: { - type: `bar`, - stacked: true, - }, - }); + if (created) { + this.close(); + if (this.#diceNamespaceAlert) { + ui.notifications.remove(this.#diceNamespaceAlert); + this.#diceNamespaceAlert = null; + }; + }; }; }; From 0b89b0e54edbc810fc4ab0074c0ab80207ed7775 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 10:25:20 -0600 Subject: [PATCH 38/77] Ensure that the manager doesn't error while prepping string buckets without a pre-existing choices config (closes #26) --- module/Apps/TableManager.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/Apps/TableManager.mjs b/module/Apps/TableManager.mjs index 2bd2e82..1bb6726 100644 --- a/module/Apps/TableManager.mjs +++ b/module/Apps/TableManager.mjs @@ -210,7 +210,7 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { }; async _prepareStringContext(ctx, table) { - ctx.buckets.choices = [...table.buckets.choices]; + ctx.buckets.choices = [...(table.buckets.choices ?? [])]; }; // #endregion Data Prep From d11b270019515bd10680d447ff72bd1c6238f677 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 11:03:55 -0600 Subject: [PATCH 39/77] Throw a more clear error when the compendia build fails --- vite.config.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vite.config.js b/vite.config.js index da6d7ca..4f0c2e1 100644 --- a/vite.config.js +++ b/vite.config.js @@ -68,7 +68,11 @@ function buildPacks() { return { async writeBundle(options) { const buildDir = options.dir; - await buildCompendia(); + try { + await buildCompendia(); + } catch { + throw new Error(`Compendium building failed, make sure Foundry isn't running`); + }; await cp(`${__dirname}/packs`, `${buildDir}/packs`, { recursive: true, force: true }); for (const file of glob.sync(`${buildDir}/packs/**/_source/`)) { await rm(file, { recursive: true, force: true }); From de69fdec0f947ab99334d5d1e455405ada7eceac Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 11:04:43 -0600 Subject: [PATCH 40/77] Update vite to make dependabot happier --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 441ebfc..a88df2c 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,7 @@ "lint:nofix": "eslint", "dev": "NODE_ENV=development vite build --mode dev --watch", "dev:once": "NODE_ENV=development vite build --mode dev", - "staging": "NODE_ENV=staging vite build --mode staging --watch", - "staging:once": "NODE_ENV=staging vite build --mode staging", + "staging": "NODE_ENV=staging vite build --mode staging", "build": "NODE_ENV=production vite build --mode prod" }, "devDependencies": { @@ -17,7 +16,7 @@ "eslint": "^9.25.0", "glob": "^11.0.1", "terser": "^5.39.0", - "vite": "^6.3.1" + "vite": "^6.3.4" }, "dependencies": { "chart.js": "^4.4.9", From af2dac394f6fb11b9c347adefb8ec147d9179316 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 13:26:09 -0600 Subject: [PATCH 41/77] Update the throw to not include the stack trace --- vite.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vite.config.js b/vite.config.js index 4f0c2e1..aa24662 100644 --- a/vite.config.js +++ b/vite.config.js @@ -71,7 +71,9 @@ function buildPacks() { try { await buildCompendia(); } catch { - throw new Error(`Compendium building failed, make sure Foundry isn't running`); + const err = new Error(`Compendium building failed, make sure Foundry isn't running`); + err.stack = ``; + throw err; }; await cp(`${__dirname}/packs`, `${buildDir}/packs`, { recursive: true, force: true }); for (const file of glob.sync(`${buildDir}/packs/**/_source/`)) { From d11262ad014992da148664832ad31ed1c27385b5 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 15:32:22 -0600 Subject: [PATCH 42/77] Make the bucket validator throw an error rather than returning a weird value --- module/utils/buckets.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/module/utils/buckets.mjs b/module/utils/buckets.mjs index c689445..66a3913 100644 --- a/module/utils/buckets.mjs +++ b/module/utils/buckets.mjs @@ -52,8 +52,7 @@ export function validateBucketConfig(config) { const validator = validators[conf.type]; if (validator == null) { - Logger.error(`Failed to find type validator for: ${conf.type}`); - return false; + throw new Error(`Failed to find type validator for: ${conf.type}`); }; // Disallow function choices if present From 60b01c55e174de73c292374e7883e5254beb1f68 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 15:33:40 -0600 Subject: [PATCH 43/77] Fix undefined reference error when updating a table (closes #27) --- module/utils/databases/Database.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/module/utils/databases/Database.mjs b/module/utils/databases/Database.mjs index 1a55f62..acca180 100644 --- a/module/utils/databases/Database.mjs +++ b/module/utils/databases/Database.mjs @@ -131,8 +131,8 @@ export class Database { return false; }; - const table = this.getTable(tableID); - if (!tables[tableID]) { + const table = await this.getTable(tableID); + if (!table) { ui.notifications.error(`Cannot update table that doesn't exist`); return false; }; @@ -144,7 +144,7 @@ export class Database { const diff = diffObject( table, expandObject(changes), - { inner: true, deletionKeys: true }, + { deletionKeys: true }, ); if (Object.keys(diff).length === 0) { return false }; From d49998801fb75a9d36f1e2dfc2d6fdbef2fbd529 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 17:28:23 -0600 Subject: [PATCH 44/77] Remove TODO since it is handled by the watcher plugin --- vite.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/vite.config.js b/vite.config.js index aa24662..8ef2ae5 100644 --- a/vite.config.js +++ b/vite.config.js @@ -142,7 +142,6 @@ export default defineConfig(({ mode }) => { rollupOptions: { input: { module: `./module/main.mjs`, - // TODO: Figure out how to get handlebars files being used here }, output: { entryFileNames: `[name].mjs`, From 22036c419d05ff67e4c0acae2b415c583ba148b5 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 23:15:24 -0600 Subject: [PATCH 45/77] Begin writing tests --- module/__tests__/README.md | 5 ++ module/__tests__/registration.mjs | 9 ++++ module/__tests__/schemas/barGraph.mjs | 51 +++++++++++++++++++ module/__tests__/schemas/numberBucket.mjs | 61 +++++++++++++++++++++++ module/__tests__/schemas/stringBucket.mjs | 40 +++++++++++++++ module/main.mjs | 6 +++ 6 files changed, 172 insertions(+) create mode 100644 module/__tests__/README.md create mode 100644 module/__tests__/registration.mjs create mode 100644 module/__tests__/schemas/barGraph.mjs create mode 100644 module/__tests__/schemas/numberBucket.mjs create mode 100644 module/__tests__/schemas/stringBucket.mjs diff --git a/module/__tests__/README.md b/module/__tests__/README.md new file mode 100644 index 0000000..e90289f --- /dev/null +++ b/module/__tests__/README.md @@ -0,0 +1,5 @@ +## Testing + +The stat-tracker module utilizes [quench](https://foundryvtt.com/packages/quench) +for it's end-to-end tests and unit tests, enabling us to be sure that the module +is as stable as possible and detect when there are breaking changes. diff --git a/module/__tests__/registration.mjs b/module/__tests__/registration.mjs new file mode 100644 index 0000000..787c4f1 --- /dev/null +++ b/module/__tests__/registration.mjs @@ -0,0 +1,9 @@ +import { barGraphTests } from "./schemas/barGraph.mjs"; +import { numberBucketTests } from "./schemas/numberBucket.mjs"; +import { stringBucketTests } from "./schemas/stringBucket.mjs"; + +Hooks.on(`quenchReady`, (quench) => { + numberBucketTests(quench); + stringBucketTests(quench); + barGraphTests(quench); +}); diff --git a/module/__tests__/schemas/barGraph.mjs b/module/__tests__/schemas/barGraph.mjs new file mode 100644 index 0000000..a2b9fea --- /dev/null +++ b/module/__tests__/schemas/barGraph.mjs @@ -0,0 +1,51 @@ +import { api } from "../../api.mjs"; + +export function barGraphTests(quench) { + quench.registerBatch( + `${__ID__}.barGraphSchema`, + (ctx) => { + const { describe, it, expect } = ctx; + + describe(`the bar graph schema`, () => { + it(`should default any additional properties left out`, () => { + const { value, error } = api.schemas.graphs.bar.validate( + { type: `bar` }, + ); + expect(value).to.have.keys(`type`, `stacked`, `showEmptyBuckets`); + expect(error).to.be.undefined; + }); + + it(`should allow stacked to be provided specifically`, () => { + const { value, error } = api.schemas.graphs.bar.validate( + { type: `bar`, stacked: true }, + ); + expect(value).to.have.keys(`type`, `stacked`, `showEmptyBuckets`); + expect(error).to.be.undefined; + }); + + it(`should allow showEmptyBuckets to be provided specifically`, () => { + const { value, error } = api.schemas.graphs.bar.validate( + { type: `bar`, showEmptyBuckets: true }, + ); + expect(value).to.have.keys(`type`, `stacked`, `showEmptyBuckets`); + expect(error).to.be.undefined; + }); + + it(`should only allow showEmptyBuckets to be a boolean`, () => { + const { value, error } = api.schemas.graphs.bar.validate( + { type: `bar`, showEmptyBuckets: `a potato` }, + ); + expect(value).to.have.keys(`type`, `stacked`, `showEmptyBuckets`); + expect(error).not.to.be.undefined; + }); + + it(`should only allow stacked to be a boolean`, () => { + const { error } = api.schemas.graphs.bar.validate( + { type: `bar`, stacked: `a potato` }, + ); + expect(error).not.to.be.undefined; + }); + }); + }, + ); +}; diff --git a/module/__tests__/schemas/numberBucket.mjs b/module/__tests__/schemas/numberBucket.mjs new file mode 100644 index 0000000..2ebba9a --- /dev/null +++ b/module/__tests__/schemas/numberBucket.mjs @@ -0,0 +1,61 @@ +import { api } from "../../api.mjs"; + +export function numberBucketTests(quench) { + quench.registerBatch( + `${__ID__}.numberBucketSchema`, + (ctx) => { + const { describe, it, expect } = ctx; + + describe(`the number bucket schema`, () => { + it(`should allow all additional properties to be left out`, () => { + const { error } = api.schemas.buckets.number.validate( + { type: `number` }, + ); + expect(error).to.be.undefined; + }); + + it(`should allow the min additional property if only it is provided with the type`, () => { + const { error } = api.schemas.buckets.number.validate( + { type: `number`, min: 0 }, + ); + expect(error).to.be.undefined; + }); + + it(`should allow the max additional property if only it is provided with the type`, () => { + const { error } = api.schemas.buckets.number.validate( + { type: `number`, max: 10 }, + ); + expect(error).to.be.undefined; + }); + + it(`should not allow the step additional property if only it is provided with the type`, () => { + const { error } = api.schemas.buckets.number.validate( + { type: `number`, step: 1 }, + ); + expect(error).not.to.be.undefined; + }); + + it(`should not allow max to be less than min`, () => { + const { error } = api.schemas.buckets.number.validate( + { type: `number`, min: 10, max: 5 }, + ); + expect(error).not.to.be.undefined; + }); + + it(`should not allow max to be less than min`, () => { + const { error } = api.schemas.buckets.number.validate( + { type: `number`, min: 10, max: 15 }, + ); + expect(error).to.be.undefined; + }); + + it(`should allow step when min is also provided`, () => { + const { error } = api.schemas.buckets.number.validate( + { type: `number`, min: 10, step: 5 }, + ); + expect(error).to.be.undefined; + }); + }); + }, + ); +}; diff --git a/module/__tests__/schemas/stringBucket.mjs b/module/__tests__/schemas/stringBucket.mjs new file mode 100644 index 0000000..d992d63 --- /dev/null +++ b/module/__tests__/schemas/stringBucket.mjs @@ -0,0 +1,40 @@ +import { api } from "../../api.mjs"; + +export function stringBucketTests(quench) { + quench.registerBatch( + `${__ID__}.stringBucketSchema`, + (ctx) => { + const { describe, it, expect } = ctx; + + describe(`the string bucket schema`, () => { + it(`should allow all additional properties to be left out`, () => { + const { error } = api.schemas.buckets.string.validate( + { type: `string` }, + ); + expect(error).to.be.undefined; + }); + + it(`should allow specific choices to be provided`, () => { + const { error } = api.schemas.buckets.string.validate( + { type: `string`, choices: [`choice 1`, `choice 2`] }, + ); + expect(error).to.be.undefined; + }); + + it(`shouldn't allow specific choices to be empty`, () => { + const { error } = api.schemas.buckets.string.validate( + { type: `string`, choices: [] }, + ); + expect(error).not.to.be.undefined; + }); + + it(`should only allow specific choices to be strings`, () => { + const { error } = api.schemas.buckets.string.validate( + { type: `string`, choices: [`choice 1`, 5] }, + ); + expect(error).not.to.be.undefined; + }); + }); + }, + ); +}; diff --git a/module/main.mjs b/module/main.mjs index b3a76f4..11bdbcb 100644 --- a/module/main.mjs +++ b/module/main.mjs @@ -6,3 +6,9 @@ import "./hooks/ready.mjs"; // Document Hooks import "./hooks/preCreateChatMessage.mjs"; + +// Dev Only imports +import "./__tests__/registration.mjs"; +// if (import.meta.env.DEV) { +// import(`./__tests__/registration.mjs`); +// } From 5fe11fda0daa398db6ea2f1f44f92a3a734a3e71 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 23:15:42 -0600 Subject: [PATCH 46/77] Update db schemas --- module/utils/databases/model.mjs | 57 ++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/module/utils/databases/model.mjs b/module/utils/databases/model.mjs index 0461055..ed83e23 100644 --- a/module/utils/databases/model.mjs +++ b/module/utils/databases/model.mjs @@ -3,31 +3,25 @@ import { PrivacyMode } from "../privacy.mjs"; // MARK: Buckets export const numberBucketSchema = Joi.object({ - type: Joi.string().valid(`number`, `range`).required(), + type: Joi.string().valid(`number`).required(), min: Joi .number() .integer() - .when(`type`, { - is: Joi.string().valid(`range`), + .when(`step`, { + is: Joi.exist(), then: Joi.required(), - otherwise: Joi.optional(), }), max: Joi .number() .integer() - .when(`type`, { - is: Joi.string().valid(`range`), - then: Joi.required(), - otherwise: Joi.optional(), + .when(`min`, { + is: Joi.exist(), + then: Joi.number().greater(Joi.ref(`min`)), }), step: Joi .number() .integer() - .when(`type`, { - is: Joi.string().valid(`range`), - then: Joi.required(), - otherwise: Joi.optional(), - }), + .min(1), }); export const stringBucketSchema = Joi.object({ @@ -37,16 +31,15 @@ export const stringBucketSchema = Joi.object({ .items( Joi.string().trim().invalid(``), ) + .min(1) .optional(), }); // MARK: Graphs export const barGraphSchema = Joi.object({ type: Joi.string().valid(`bar`).required(), - stacked: Joi - .boolean() - .default(true) - .optional(), + stacked: Joi.boolean().optional().default(true), + showEmptyBuckets: Joi.boolean().optional().default(false), }); // MARK: Table @@ -59,18 +52,34 @@ export const tableSchema = Joi.object({ .pattern(/^[0-9a-z \-_]+(\/[0-9a-z \-_]+)?$/i), buckets: Joi .alternatives() - .try( - numberBucketSchema, - stringBucketSchema, + .conditional( + `/buckets.type`, + { + switch: [ + { + is: `number`, + then: numberBucketSchema, + }, + { + is: `string`, + then: stringBucketSchema, + }, + ], + otherwise: Joi.forbidden(), + }, ) - .match(`one`) .required(), graph: Joi .alternatives() - .try( - barGraphSchema, + .conditional( + `/graph.type`, + { + switch: [ + { is: `bar`, then: barGraphSchema }, + ], + otherwise: Joi.forbidden(), + }, ) - .match(`one`) .required(), }); From ac93a3342f514ae88f6db6879dc80f3069b51b60 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 23:16:13 -0600 Subject: [PATCH 47/77] Begin work on purging the range bucket type from the codebase --- module/api.mjs | 1 - module/utils/buckets.mjs | 33 ++------------------------------ module/utils/databases/utils.mjs | 3 ++- 3 files changed, 4 insertions(+), 33 deletions(-) diff --git a/module/api.mjs b/module/api.mjs index 0dbaf9d..539e15a 100644 --- a/module/api.mjs +++ b/module/api.mjs @@ -38,7 +38,6 @@ export const api = deepFreeze({ }, schemas: { buckets: { - range: numberBucketSchema, number: numberBucketSchema, string: stringBucketSchema, }, diff --git a/module/utils/buckets.mjs b/module/utils/buckets.mjs index 66a3913..7082df1 100644 --- a/module/utils/buckets.mjs +++ b/module/utils/buckets.mjs @@ -61,7 +61,7 @@ export function validateBucketConfig(config) { delete conf.choices; }; - validator.validateConfig(conf); + validator.validateConfig?.(conf); return conf; }; @@ -74,7 +74,7 @@ const validators = { opts.trim = true; opts.blank = false; }, - validateConfig: (config) => { + transformConfig: (config) => { if (config.choices.length === 0) { delete config.choices; config[`-=choices`] = null; @@ -84,35 +84,6 @@ const validators = { [BucketTypes.NUMBER]: { field: NumberField, transformOptions: transformNumberFieldOptions, - validateConfig: (config) => { - if (config.step != null && config.min == null) { - delete config.step; - config[`-=step`] = null; - }; - if ( - config.min != null - && config.max != null - && config.min > config.max - ) { - throw new Error(`"min" must be less than "max"`); - } - }, - }, - [BucketTypes.RANGE]: { - field: NumberField, - transformOptions: transformNumberFieldOptions, - validateConfig: (config) => { - if (config.min == null) { - throw new Error(`"min" must be defined for range buckets`); - }; - if (config.max == null) { - throw new Error(`"max" must be defined for range buckets`); - }; - if (config.min > config.max) { - throw new Error(`"min" must be less than "max"`); - } - config.step ??= 1; - }, }, }; diff --git a/module/utils/databases/utils.mjs b/module/utils/databases/utils.mjs index ba4cf69..58c3907 100644 --- a/module/utils/databases/utils.mjs +++ b/module/utils/databases/utils.mjs @@ -2,7 +2,7 @@ export function createDiceTable(size) { return { name: `Dice/d${size}`, buckets: { - type: `range`, + type: `number`, min: 1, max: size, step: 1, @@ -10,6 +10,7 @@ export function createDiceTable(size) { graph: { type: `bar`, stacked: true, + showEmptyBuckets: true, }, }; }; From 946a44edaef074d73ceaf7ecdcaec315cb9e2634 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sat, 31 May 2025 23:17:38 -0600 Subject: [PATCH 48/77] Add missing import into the extraction script --- scripts/extractCompendia.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/extractCompendia.mjs b/scripts/extractCompendia.mjs index 84dd87d..e119e83 100644 --- a/scripts/extractCompendia.mjs +++ b/scripts/extractCompendia.mjs @@ -1,6 +1,7 @@ import { readFile } from "fs/promises"; import { join } from "path"; import { extractPack } from "@foundryvtt/foundryvtt-cli"; +import { pathToFileURL } from "url"; export async function extractCompendia() { const manifest = JSON.parse(await readFile(`./public/module.json`, `utf-8`)); From c26b4318eeaad7117566d1e0ffd5dca77dbdd717 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 11:24:20 -0600 Subject: [PATCH 49/77] Finish writing the schema tests --- module/__tests__/registration.mjs | 4 ++ module/__tests__/schemas/row.mjs | 96 ++++++++++++++++++++++++++++++ module/__tests__/schemas/table.mjs | 43 +++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 module/__tests__/schemas/row.mjs create mode 100644 module/__tests__/schemas/table.mjs diff --git a/module/__tests__/registration.mjs b/module/__tests__/registration.mjs index 787c4f1..83e025c 100644 --- a/module/__tests__/registration.mjs +++ b/module/__tests__/registration.mjs @@ -1,9 +1,13 @@ import { barGraphTests } from "./schemas/barGraph.mjs"; import { numberBucketTests } from "./schemas/numberBucket.mjs"; +import { rowTests } from "./schemas/row.mjs"; import { stringBucketTests } from "./schemas/stringBucket.mjs"; +import { tableTests } from "./schemas/table.mjs"; Hooks.on(`quenchReady`, (quench) => { numberBucketTests(quench); stringBucketTests(quench); barGraphTests(quench); + tableTests(quench); + rowTests(quench); }); diff --git a/module/__tests__/schemas/row.mjs b/module/__tests__/schemas/row.mjs new file mode 100644 index 0000000..394e42e --- /dev/null +++ b/module/__tests__/schemas/row.mjs @@ -0,0 +1,96 @@ +import { api } from "../../api.mjs"; +import { PrivacyMode } from "../../utils/privacy.mjs"; + +export function rowTests(quench) { + quench.registerBatch( + `${__ID__}.rowSchema`, + (ctx) => { + const { describe, it, expect } = ctx; + + describe(`the row schema`, () => { + it(`should allow number-based values`, () => { + const { error } = api.schemas.row.validate( + { + _id: `1`, + timestamp: (new Date()).toISOString(), + value: 1, + privacy: PrivacyMode.PUBLIC, + }, + ); + expect(error).to.be.undefined; + }); + + it(`should allow string-based values`, () => { + const { error } = api.schemas.row.validate( + { + _id: `1`, + timestamp: (new Date()).toISOString(), + value: `apple`, + privacy: PrivacyMode.PUBLIC, + }, + ); + expect(error).to.be.undefined; + }); + + it(`shouldn't allow invalid privacy modes`, () => { + const { error } = api.schemas.row.validate( + { + _id: `1`, + timestamp: (new Date()).toISOString(), + value: 1, + privacy: `yahaha`, + }, + ); + expect(error).not.to.be.undefined; + }); + + it(`shouldn't allow invalid value modes`, () => { + const { error } = api.schemas.row.validate( + { + _id: `1`, + timestamp: (new Date()).toISOString(), + value: true, + privacy: PrivacyMode.PUBLIC, + }, + ); + expect(error).not.to.be.undefined; + }); + + it(`shouldn't allow non-ISO date formats`, () => { + const { error } = api.schemas.row.validate( + { + _id: `1`, + timestamp: (new Date()).toDateString(), + value: 1, + privacy: PrivacyMode.PUBLIC, + }, + ); + expect(error).not.to.be.undefined; + }); + + it(`should require an ID to be present`, () => { + const { error } = api.schemas.row.validate( + { + timestamp: (new Date()).toISOString(), + value: true, + privacy: PrivacyMode.PUBLIC, + }, + ); + expect(error).not.to.be.undefined; + }); + + it(`shouldn't allow empty string as a value`, () => { + const { error } = api.schemas.row.validate( + { + _id: `1`, + timestamp: (new Date()).toISOString(), + value: ``, + privacy: PrivacyMode.PUBLIC, + }, + ); + expect(error).not.to.be.undefined; + }); + }); + }, + ); +}; diff --git a/module/__tests__/schemas/table.mjs b/module/__tests__/schemas/table.mjs new file mode 100644 index 0000000..c3d430e --- /dev/null +++ b/module/__tests__/schemas/table.mjs @@ -0,0 +1,43 @@ +import { api } from "../../api.mjs"; + +const graph = { type: `bar` }; +const buckets = { type: `string` }; + +export function tableTests(quench) { + quench.registerBatch( + `${__ID__}.tableSchema`, + (ctx) => { + const { describe, it, expect } = ctx; + + describe(`the table schema`, () => { + it(`should require that name be a non-empty string`, () => { + const { error } = api.schemas.table.validate( + { name: ``, graph, buckets }, + ); + expect(error).not.to.be.undefined; + }); + + it(`should require that name only contain alphanumeric characters`, () => { + const { error } = api.schemas.table.validate( + { name: `:(`, graph, buckets }, + ); + expect(error).not.to.be.undefined; + }); + + it(`should allow the name to contain spaces`, () => { + const { error } = api.schemas.table.validate( + { name: `a name with spaces`, graph, buckets }, + ); + expect(error).to.be.undefined; + }); + + it(`should allow a single forward slash for subtables`, () => { + const { error } = api.schemas.table.validate( + { name: `Table/subtable`, graph, buckets }, + ); + expect(error).to.be.undefined; + }); + }); + }, + ); +}; From 965cb26b51164c2a10b8840fb0788d953668c1ab Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 11:25:10 -0600 Subject: [PATCH 50/77] Update the tests import not to be bundled for production --- module/main.mjs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/module/main.mjs b/module/main.mjs index 11bdbcb..12d5422 100644 --- a/module/main.mjs +++ b/module/main.mjs @@ -8,7 +8,6 @@ import "./hooks/ready.mjs"; import "./hooks/preCreateChatMessage.mjs"; // Dev Only imports -import "./__tests__/registration.mjs"; -// if (import.meta.env.DEV) { -// import(`./__tests__/registration.mjs`); -// } +if (import.meta.env.DEV) { + import(`./__tests__/registration.mjs`); +}; From c6161dd312793f7986b1012d0d479d62f5f212ea Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 12:26:40 -0600 Subject: [PATCH 51/77] Prevent errors when the flag is undefined on the user --- module/utils/databases/UserFlag.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/module/utils/databases/UserFlag.mjs b/module/utils/databases/UserFlag.mjs index 7072c5c..41ed866 100644 --- a/module/utils/databases/UserFlag.mjs +++ b/module/utils/databases/UserFlag.mjs @@ -27,7 +27,7 @@ export class UserFlagDatabase extends Database { return false; }; - const userData = user.getFlag(__ID__, dataFlag); + const userData = user.getFlag(__ID__, dataFlag) ?? {}; userData[tableID] ??= []; userData[tableID].push(corrected); await user.setFlag(__ID__, dataFlag, userData); @@ -146,7 +146,6 @@ export class UserFlagDatabase extends Database { if (this.#listener !== null) { return }; this.#listener = Hooks.on(`updateUser`, (doc, diff, options, userID) => { - Logger.debug({ diff, userID, doc }); // Shortcircuit when on the client that triggered the update if (userID === game.user.id) { return }; if (!hasProperty(diff, `flags.${__ID__}.${dataFlag}`)) { return }; From 21b9cf5b2daaefc7089828fdd5b3c0503d257e6b Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 13:03:03 -0600 Subject: [PATCH 52/77] Remove logs that aren't helpful for prod --- module/Apps/TableCreator.mjs | 1 - module/Apps/TableManager.mjs | 1 - module/utils/databases/Memory.mjs | 2 -- module/utils/privacy.mjs | 1 - 4 files changed, 5 deletions(-) diff --git a/module/Apps/TableCreator.mjs b/module/Apps/TableCreator.mjs index 4512ad3..9619af2 100644 --- a/module/Apps/TableCreator.mjs +++ b/module/Apps/TableCreator.mjs @@ -104,7 +104,6 @@ export class TableCreator extends HandlebarsApplicationMixin(ApplicationV2) { return; }; - Logger.log(`updating ${binding} value to ${target.value}`); this[binding] = target.value; this.render(); }; diff --git a/module/Apps/TableManager.mjs b/module/Apps/TableManager.mjs index 1bb6726..f413805 100644 --- a/module/Apps/TableManager.mjs +++ b/module/Apps/TableManager.mjs @@ -253,7 +253,6 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { */ static async #deleteTable() { const table = await CONFIG.stats.db.getTable(this.activeTableID); - Logger.debug({ table }); if (!table) { ui.notifications.error( `You must select a table before you can delete it`, diff --git a/module/utils/databases/Memory.mjs b/module/utils/databases/Memory.mjs index 8dba510..b32555a 100644 --- a/module/utils/databases/Memory.mjs +++ b/module/utils/databases/Memory.mjs @@ -1,7 +1,6 @@ import { filterPrivateRows, PrivacyMode } from "../privacy.mjs"; import { createDiceTable } from "./utils.mjs"; import { Database } from "./Database.mjs"; -import { Logger } from "../Logger.mjs"; import { validateBucketConfig } from "../buckets.mjs"; const { deleteProperty, diffObject, expandObject, mergeObject, randomID } = foundry.utils; @@ -130,7 +129,6 @@ export class MemoryDatabase extends Database { row._id ||= randomID(); row.timestamp = new Date().toISOString(); - Logger.debug(`Adding row:`, row); this.#rows[userID][table].push(row); if (rerender) { this.render({ userUpdated: userID }); diff --git a/module/utils/privacy.mjs b/module/utils/privacy.mjs index 20ed3ff..f04a885 100644 --- a/module/utils/privacy.mjs +++ b/module/utils/privacy.mjs @@ -31,7 +31,6 @@ export function determinePrivacyFromRollMode(rollMode) { * @returns The filtered rows */ export function filterPrivateRows(rows, userID, privacies) { - console.log({rows, userID, privacies}); const filtered = []; const isMe = userID === game.user.id; From 6ef20e1ec1aba140f00c8908c3b7a84133bb7c81 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 13:03:13 -0600 Subject: [PATCH 53/77] Remove action that I haven't implemented yet --- module/Apps/StatsViewer.mjs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/module/Apps/StatsViewer.mjs b/module/Apps/StatsViewer.mjs index 57a1cd8..152be1c 100644 --- a/module/Apps/StatsViewer.mjs +++ b/module/Apps/StatsViewer.mjs @@ -21,10 +21,10 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { resizable: true, minimizable: true, controls: [ - { - label: `Add All Users To Graph`, - action: `addAllUsers`, - }, + // { + // label: `Add All Users To Graph`, + // action: `addAllUsers`, + // }, ], }, position: { From bd4c32f65a13a61cc98c425c19546a9a5351590e Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 13:03:31 -0600 Subject: [PATCH 54/77] Update documentation (closes #30) --- packs/docs/_source/English_pBOyeBDuTeowuDOE.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json index cba21ab..91393f7 100644 --- a/packs/docs/_source/English_pBOyeBDuTeowuDOE.json +++ b/packs/docs/_source/English_pBOyeBDuTeowuDOE.json @@ -133,7 +133,7 @@ "image": {}, "text": { "format": 1, - "content": "

A bucket is the term used to identify a group of allowed values within a @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{table}, each bucket must have a type, and a number of additional settings depending on what type it is.

String Buckets

This is the most simple type of bucket, it allows a string to be added as the row's value. The only additional configuration for this type of bucket is restricting what strings can be added be added.

e.g. you can limit each row to only have a value of \"Critical Success\", or \"Critical Failure\" and if someone tries to add \"Apple Sauce\" into the table, it will reject that row.

Number Buckets

This type of bucket is likely the one you will utilize the most, it allows storing any number. It accepts an set of additional options described below, all of which are optional.

Setting

Description

Minimum

The minimum allowed value

Maximum

The maximum allowed value, must be greater than Minimum

Step

Requires Minimum

When a step is set it requires each number to be a \"step\" away from the lower one. So if you have a minimum of 2 and a step of 4, the allowed values are: 2, 6, 10, 14, 18, etc.

Range Buckets

The range bucket is what is used by the auto-generated dice tables, these bucket types use the same options as the @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.e9FYKidbfFnnTspO#number-buckets]{Number Buckets} but the Minimum/Maximum/Step options are required when the type is range.

" + "content": "

A bucket is the term used to identify a group of allowed values within a @UUID[Compendium.stat-tracker.docs.JournalEntry.pBOyeBDuTeowuDOE.JournalEntryPage.ugzCCxQskUSYMZR4]{table}, each bucket must have a type, and a number of additional settings depending on what type it is.

String Buckets

This is the most simple type of bucket, it allows a string to be added as the row's value. The only additional configuration for this type of bucket is restricting what strings can be added be added.

e.g. you can limit each row to only have a value of \"Critical Success\", or \"Critical Failure\" and if someone tries to add \"Apple Sauce\" into the table, it will reject that row.

Number Buckets

This type of bucket is likely the one you will utilize the most, it allows storing any number. It accepts an set of additional options described below, all of which are optional.

Setting

Description

Minimum

The minimum allowed value.

Required when Step is provided.

Maximum

The maximum allowed value, must be greater than Minimum

Step

When a step is set it requires each number to be a \"step\" away from the lower one. So if you have a minimum of 2 and a step of 4, the allowed values are: 2, 6, 10, 14, 18, etc.

" }, "video": { "controls": true, @@ -153,7 +153,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748329573212, - "modifiedTime": 1748499573438, + "modifiedTime": 1748803873692, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.e9FYKidbfFnnTspO" @@ -348,7 +348,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748393806045, - "modifiedTime": 1748499640505, + "modifiedTime": 1748803890586, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.IXZpEBEJsvOpY3OL" @@ -406,7 +406,7 @@ "image": {}, "text": { "format": 1, - "content": "

All of these enums are available within <api>.enums, they are read-only and cannot be modified by other plugins.

Privacy Modes

This enum is used by the module to specify all of the privacy levels that it uses.

The valid values are:

  • GM - Representing that only gamemasters and assistant gamemasters will be able to see this data entry. This mode is similar to the \"Blind GM Roll\" roll mode.

  • PRIVATE - Indicating that this is a piece of private data, that only gamemasters, assistant gamemasters, and the user who owns the piece of data will be able to see it.

  • SELF - Similar to the \"GM\" level, but instead of gamemasters, it's only the user who owns the piece of data that's able to see it.

  • PUBLIC - Everyone can see it.

" + "content": "

All of these enums are available within <api>.enums, they are read-only and cannot be modified by other plugins.

Privacy Modes

This enum is used by the module to specify all of the privacy levels that it uses.

The valid values are:

  • GM - Representing that only gamemasters and assistant gamemasters will be able to see this data entry. This mode is similar to the \"Blind GM Roll\" roll mode.

  • PRIVATE - Indicating that this is a piece of private data, that only gamemasters, assistant gamemasters, and the user who owns the piece of data will be able to see it.

  • SELF - Similar to the \"GM\" level, but instead of gamemasters, it's only the user who owns the piece of data that's able to see it.

  • PUBLIC - Everyone can see it.

" }, "video": { "controls": true, @@ -426,7 +426,7 @@ "systemId": "empty-system", "systemVersion": "0.0.0", "createdTime": 1748394110971, - "modifiedTime": 1748657380240, + "modifiedTime": 1748803937108, "lastModifiedBy": "t2sWGWEYSMFrfBu3" }, "_key": "!journal.pages!pBOyeBDuTeowuDOE.WYaZPgSRDx8L7Zmq" From 3d5e28189afe3b42f71ba0bfa433e337abeacd46 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 14:23:18 -0600 Subject: [PATCH 55/77] Version bump --- public/module.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/module.json b/public/module.json index be69283..91289af 100644 --- a/public/module.json +++ b/public/module.json @@ -1,7 +1,7 @@ { "id": "stat-tracker", "title": "Stats Tracker", - "version": "1.0.1", + "version": "1.0.2", "compatibility": { "maximum": 13, "verified": 13, From fc3b04146460df83ab40aa0a349e2aceb82bbb86 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 14:23:21 -0600 Subject: [PATCH 56/77] Expose the determinePrivacyFromRollMode within the API --- module/api.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/module/api.mjs b/module/api.mjs index 539e15a..9faffaa 100644 --- a/module/api.mjs +++ b/module/api.mjs @@ -11,7 +11,7 @@ import { UserFlagDatabase } from "./utils/databases/UserFlag.mjs"; // Utils import { barGraphSchema, numberBucketSchema, rowSchema, stringBucketSchema, tableSchema } from "./utils/databases/model.mjs"; -import { filterPrivateRows, PrivacyMode } from "./utils/privacy.mjs"; +import { determinePrivacyFromRollMode, filterPrivateRows, PrivacyMode } from "./utils/privacy.mjs"; import { validateBucketConfig, validateValue } from "./utils/buckets.mjs"; const { deepFreeze } = foundry.utils; @@ -24,6 +24,7 @@ export const api = deepFreeze({ TableManager, }, utils: { + determinePrivacyFromRollMode, filterPrivateRows, validateValue, validateBucketConfig, From 8e83925abe51f7df886abda08cd79fee6d3ad68d Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 14:59:06 -0600 Subject: [PATCH 57/77] Have the UserFlagDatabase validate the row's value according to the bucket schema during creation / updating --- module/utils/databases/UserFlag.mjs | 45 ++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/module/utils/databases/UserFlag.mjs b/module/utils/databases/UserFlag.mjs index 41ed866..c5292cd 100644 --- a/module/utils/databases/UserFlag.mjs +++ b/module/utils/databases/UserFlag.mjs @@ -2,6 +2,7 @@ import { filterPrivateRows, PrivacyMode } from "../privacy.mjs"; import { Database } from "./Database.mjs"; import { Logger } from "../Logger.mjs"; import { rowSchema } from "./model.mjs"; +import { validateValue } from "../buckets.mjs"; const { hasProperty, mergeObject, randomID } = foundry.utils; @@ -46,6 +47,9 @@ export class UserFlagDatabase extends Database { const userData = user.getFlag(__ID__, dataFlag) ?? {}; userData[tableID] ??= []; + let valueErrorPosted = false; + let validationErrorPosted = false; + for (const row of rows) { row._id = randomID(); row.timestamp = new Date().toISOString(); @@ -55,12 +59,31 @@ export class UserFlagDatabase extends Database { { abortEarly: false, convert: true, dateFormat: `iso`, render: false }, ); if (error) { - ui.notifications.error(`A row being created did not conform to required schema, see console for more information.`, { console: false }); + if (!validationErrorPosted) { + ui.notifications.error( + `One or more rows being created did not conform to required schema, skipping row and see console for more information.`, + { console: false }, + ); + validationErrorPosted = true; + }; Logger.error(`Failing row:`, row); Logger.error(error); continue; }; + const validValue = validateValue(corrected.value, table.buckets); + if (!validValue) { + if (!valueErrorPosted) { + ui.notifications.warn( + `One or more rows being created did not contain a valid value, skipping row and see console for more information.`, + { console: false }, + ); + valueErrorPosted = true; + }; + Logger.warn(`Row with invalid value:`, row); + continue; + }; + userData[tableID].push(corrected); }; @@ -100,22 +123,36 @@ export class UserFlagDatabase extends Database { const table = await this.getTable(tableID); if (!table) { Logger.error(`Cannot find the table with ID "${tableID}"`); - return; + return false; }; const user = game.users.get(userID); if (!user) { Logger.error(`Can't find the user with ID "${tableID}"`); - return; + return false; }; const userData = user.getFlag(__ID__, dataFlag) ?? {}; let row = userData[tableID]?.find(row => row._id === rowID); - if (!row) { return }; + if (!row) { return false }; + + if (hasProperty(changes, `value`)) { + const validValue = validateValue(changes.value, table.buckets); + if (!validValue) { + ui.notifications.warn( + `One or more rows being created did not contain a valid value, skipping row and see console for more information.`, + { console: false }, + ); + Logger.warn(`Row with invalid value:`, row); + return false; + }; + }; + mergeObject(row, changes); await user.setFlag(__ID__, dataFlag, userData); this.render({ userUpdated: userID }); this.triggerListeners(); + return true; }; static async deleteRow(tableID, userID, rowID) { From cad04690ff0c59b2aa0bf6d202797696577e4211 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 15:00:23 -0600 Subject: [PATCH 58/77] Update the release creation to provide a direct manifest url in the description of the release --- .github/workflows/draft-release.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/draft-release.yaml b/.github/workflows/draft-release.yaml index de3b830..7d0df21 100644 --- a/.github/workflows/draft-release.yaml +++ b/.github/workflows/draft-release.yaml @@ -50,5 +50,7 @@ jobs: tag: "v${{ steps.version.outputs.version }}" commit: ${{ github.ref }} draft: true + body: "This version can be installed using this manifest URL: https://github.com/Oliver-Akins/Foundry-Stat-Tracker/releases/download/v${{ steps.version.outputs.version }}/module.json" generateReleaseNotes: true artifacts: "prod.dist/release.zip,prod.dist/module.json" + artifactsErrorsFailBuild: true From ab3281b28825180b55a4ed7b8ffbbb60d899dc55 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 15:47:39 -0600 Subject: [PATCH 59/77] Update README --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ccf8f72..9027fb6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ -# Foundry-Stat-Tracker -A Foundry module that allows tracking arbitrary stats. +# Stats Tracker +This FoundryVTT module aims to provide a clean way of keeping track of any data +points you want within Foundry, whether that be dice rolls, or other things like +how many natural 1s to natural 20s you get. + +I was inspired by the dicestats module, however it only allows tracking dice +statistics, which is something I found myself needing to work around and struggle +against, so I decided to make this module to fill that gap while improving upon +the graph rendering. + +For more information on how to use this module, check out the "Documentation" +compendium within your world! + +## Installation +You can find a history of all releases on the [Foundry package listing](https://foundryvtt.com/packages/stat-tracker) +or in the [GitHub releases tab](https://github.com/Oliver-Akins/Foundry-Stat-Tracker/releases). +Prereleases will only be released to the GitHub page, so if you want to check +out those releases, you'll need to use those manifest links directly. + +## Bugs or Feature Requests +Bugs and Feature Requests can be submitted via the [GitHub issues](https://github.com/Oliver-Akins/Foundry-Stat-Tracker/issues/new/choose). +Planned features can also be seen in the GitHub issues list. + +## Contribution +Contribution guidelines / requirements coming soon. From 3fc8b654c7a42d908c9d623e8e7d77fad0bc0918 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 1 Jun 2025 16:08:34 -0600 Subject: [PATCH 60/77] Add images for the Foundry package listing --- .promo/imgs/example-dice-stat.png | Bin 0 -> 155176 bytes .promo/imgs/example-string-stat.png | Bin 0 -> 80808 bytes .promo/imgs/example-table-config.png | Bin 0 -> 38770 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .promo/imgs/example-dice-stat.png create mode 100644 .promo/imgs/example-string-stat.png create mode 100644 .promo/imgs/example-table-config.png diff --git a/.promo/imgs/example-dice-stat.png b/.promo/imgs/example-dice-stat.png new file mode 100644 index 0000000000000000000000000000000000000000..63cb50284edcc35571d7855d55907cf3e7758066 GIT binary patch literal 155176 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

#ju0~eRGhxk{8r)19_{^XxB(=d#Gs(9x-Hi?*( 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Ν_3Z@?iSRZM8#Xhrq4E3z#wwpVYp`=(69S_ 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

MA)9i}j=o7ZXjbH)c~vZBiKO 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 Y;QRMydLFBR@VzEvrQS-GiW~U-FY%2aRsaA1 literal 0 HcmV?d00001 diff --git a/.promo/imgs/example-string-stat.png b/.promo/imgs/example-string-stat.png new file mode 100644 index 0000000000000000000000000000000000000000..fa884dc4620cfe7afadf0e89a5959fc046540189 GIT binary patch literal 80808 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 gJvEzH+aems+cj?8P`qekZOBfjl9CK;Z6>5W$fI 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 X;*#Q$pQ{UO2QqlN`njxgN@xNAt(T!U literal 0 HcmV?d00001 From 8f993adb4773940e24cfe76d35f67f84fc15d7ad Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Mon, 2 Jun 2025 19:26:43 -0600 Subject: [PATCH 61/77] Finish removing the Range bucket type that was accidentally still able to be created --- module/Apps/TableCreator.mjs | 2 +- module/hooks/init.mjs | 6 +-- module/hooks/ready.mjs | 4 +- module/utils/buckets.mjs | 1 - module/utils/databases/Database.mjs | 31 ++++++++++++-- .../Apps/TableManager/buckets/range.hbs | 42 ------------------- 6 files changed, 34 insertions(+), 52 deletions(-) delete mode 100644 public/templates/Apps/TableManager/buckets/range.hbs diff --git a/module/Apps/TableCreator.mjs b/module/Apps/TableCreator.mjs index 9619af2..1422fbf 100644 --- a/module/Apps/TableCreator.mjs +++ b/module/Apps/TableCreator.mjs @@ -75,7 +75,7 @@ export class TableCreator extends HandlebarsApplicationMixin(ApplicationV2) { if (this._name.startsWith(`Dice`)) { ctx.createButtonDisabled = !this._name.match(diceNamespacePattern); ctx.typeDisabled = true; - ctx.type = BucketTypes.RANGE; + ctx.type = BucketTypes.NUMBER; this.#diceNamespaceAlert ??= ui.notifications.info( `Tables in the "Dice" namespace must be formatted as "Dice/dX" where X is the number of sides on the die and are restricted to be ranges 1 to X.`, { permanent: true }, diff --git a/module/hooks/init.mjs b/module/hooks/init.mjs index e7e1d2d..ef5bd74 100644 --- a/module/hooks/init.mjs +++ b/module/hooks/init.mjs @@ -47,9 +47,9 @@ Hooks.on(`init`, () => { manager: TableManager, }; - if (import.meta.env.DEV) { - CONFIG.stats.db = MemoryDatabase; - }; + // if (import.meta.env.DEV) { + // CONFIG.stats.db = MemoryDatabase; + // }; game.modules.get(__ID__).api = api; if (game.settings.get(__ID__, `globalAPI`)) { diff --git a/module/hooks/ready.mjs b/module/hooks/ready.mjs index 33fe483..025ed1b 100644 --- a/module/hooks/ready.mjs +++ b/module/hooks/ready.mjs @@ -29,10 +29,10 @@ Hooks.on(`ready`, () => { ); // Fire and forget - CONFIG.stats.db.migrateData(notif) + CONFIG.stats.db.migrateData(lastVersion, notif) .then(() => { game.settings.set(__ID__, `lastVersion`, __VERSION__); - setTimeout(() => ui.notifications.remove(notif), 500); + setTimeout(() => ui.notifications.remove(notif), 5_000); }); } else { ui.notifications.error( diff --git a/module/utils/buckets.mjs b/module/utils/buckets.mjs index 7082df1..53c78f6 100644 --- a/module/utils/buckets.mjs +++ b/module/utils/buckets.mjs @@ -6,7 +6,6 @@ const { StringField, NumberField } = foundry.data.fields; export const BucketTypes = { STRING: `string`, NUMBER: `number`, - RANGE: `range`, }; /** diff --git a/module/utils/databases/Database.mjs b/module/utils/databases/Database.mjs index acca180..92aa108 100644 --- a/module/utils/databases/Database.mjs +++ b/module/utils/databases/Database.mjs @@ -28,7 +28,7 @@ Default Subtables: tables that are parents to other tables. */ -const { deleteProperty, diffObject, expandObject, mergeObject } = foundry.utils; +const { deleteProperty, diffObject, expandObject, isNewerVersion, mergeObject } = foundry.utils; /** * The generic Database implementation, any subclasses should implement all of @@ -275,7 +275,11 @@ export class Database { * @returns {boolean} */ static requiresMigrationFrom(lastVersion) { - return foundry.utils.isNewerVersion(__VERSION__, lastVersion); + Logger.table({ + lastVersion, + newer: isNewerVersion(__VERSION__, lastVersion), + }); + return isNewerVersion(__VERSION__, lastVersion); }; /** @@ -289,7 +293,28 @@ export class Database { * @param {Notification} notif The progress bar notification used for * user feedback while performing migrations. */ - static async migrateData(lastVersion, notif) {}; + static async migrateData(lastVersion, notif) { + const totalSteps = 1; + Logger.debug(lastVersion); + + /* + This migration is for going up to 1.0.3, getting rid of any tables that have + a bucket type of range, since those were not supported within the initial + release, but could still accidentally be created by users. + */ + if (isNewerVersion(`1.0.3`, lastVersion)) { + Logger.log(`Migrating up to the v1.0.3 data structure`); + const tables = game.settings.get(__ID__, `tables`); + for (const table of Object.values(tables)) { + if (table.buckets.type !== `range`) { continue }; + table.buckets.type = BucketTypes.NUMBER; + table.buckets.showEmptyBuckets = true; + }; + await game.settings.set(__ID__, `tables`, tables); + notif.update({ pct: notif.pct + (1 / totalSteps) }); + }; + + }; }; /* eslint-enable no-unused-vars */ diff --git a/public/templates/Apps/TableManager/buckets/range.hbs b/public/templates/Apps/TableManager/buckets/range.hbs deleted file mode 100644 index 0462a0e..0000000 --- a/public/templates/Apps/TableManager/buckets/range.hbs +++ /dev/null @@ -1,42 +0,0 @@ -

-
- - -
-
- - -

- The size of the step between values within the range. -

-
From 1423bf097ac6a031a0306a447efaf6440744a22e Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Mon, 2 Jun 2025 23:06:30 -0600 Subject: [PATCH 62/77] Version bump --- public/module.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/module.json b/public/module.json index 91289af..8a135de 100644 --- a/public/module.json +++ b/public/module.json @@ -1,7 +1,7 @@ { "id": "stat-tracker", "title": "Stats Tracker", - "version": "1.0.2", + "version": "1.0.3", "compatibility": { "maximum": 13, "verified": 13, From 4354a25866015fd587ef9588e201d5b5ce2beda7 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Mon, 2 Jun 2025 23:10:22 -0600 Subject: [PATCH 63/77] Prevent the double-locked bucket configuration from being possible --- public/templates/Apps/TableManager/buckets/number.hbs | 11 +---------- public/templates/Apps/TableManager/buckets/string.hbs | 5 +---- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/public/templates/Apps/TableManager/buckets/number.hbs b/public/templates/Apps/TableManager/buckets/number.hbs index 91ce2ec..cebbfaa 100644 --- a/public/templates/Apps/TableManager/buckets/number.hbs +++ b/public/templates/Apps/TableManager/buckets/number.hbs @@ -1,13 +1,4 @@ -
- {{#if buckets.locked}} -

- This bucket configuration has been locked, preventing editing - of the settings. -

- {{/if}} +