diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..8b97154 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "ES2020", + "target": "ES2020" + }, + "exclude": ["node_modules", "**/node_modules/*"], + "include": ["module/**/*"], + "typeAcquisition": { + "include": ["joi"] + } +} \ No newline at end of file diff --git a/module/utils/databases/model.mjs b/module/utils/databases/model.mjs index 18b52e7..caa7b02 100644 --- a/module/utils/databases/model.mjs +++ b/module/utils/databases/model.mjs @@ -1,43 +1,60 @@ -const { fields } = foundry.data; +import * as Joi from "joi"; + +// MARK: Buckets +const numberBucketSchema = Joi.object({ + type: Joi.string().valid(`number`, `range`).required(), + min: Joi + .number() + .integer() + .when(`type`, { + is: Joi.string().valid(`range`), + then: Joi.required(), + otherwise: Joi.optional(), + }), + max: Joi + .number() + .integer() + .when(`type`, { + is: Joi.string().valid(`range`), + then: Joi.required(), + otherwise: Joi.optional(), + }), + step: Joi + .number() + .integer() + .when(`type`, { + is: Joi.string().valid(`range`), + then: Joi.required(), + otherwise: Joi.optional(), + }), +}); + +const stringBucketSchema = Joi.object({ + type: Joi.string().valid(`string`).required(), + choices: Joi.array(Joi.string()).optional(), +}); + +// MARK: Graphs +const barGraphSchema = Joi.object({ + type: Joi.string().valid(`bar`).required(), + stacked: Joi.boolean().required(), +}); // MARK: Table -export class Table extends foundry.abstract.DataModel { - static defineSchema() { - return { - name: new fields.StringField({ - nullable: false, - required: true, - blank: false, - trim: true, - validate(value) { - return !value.match(/[^a-z0-9_\-:]/i); - }, - }), - data: new fields.TypedObjectField(Row), - }; - }; -}; +export const tableSchema = Joi.object({ + name: Joi + .string() + .trim() + .required() + .pattern(/^[a-z \-_]+(\/[a-z \-_]+)?$/i), + buckets: Joi.alternatives([ + numberBucketSchema, + stringBucketSchema, + ]).match(`one`), + graph: Joi.alternatives([ + barGraphSchema, + ]).match(`one`), +}); // MARK: Row -export class Row extends foundry.abstract.DataModel { - static defineSchema() { - return { - id: new fields.StringField({ - nullable: false, - required: true, - blank: false, - }), - timestamp: new fields.NumberField({ - min: 0, - required: true, - nullable: false, - }), - value: new fields.AnyField(), - private: new fields.BooleanField({ - initial: false, - required: true, - nullable: false, - }), - }; - }; -}; +export const rowSchema = Joi.object({}); diff --git a/package-lock.json b/package-lock.json index c954e0e..9aa4a5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,8 @@ "": { "name": "stat-tracker", "dependencies": { - "chart.js": "^4.4.9" + "chart.js": "^4.4.9", + "joi": "^17.13.3" }, "devDependencies": { "@stylistic/eslint-plugin": "^4.2.0", @@ -589,6 +590,19 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1025,6 +1039,24 @@ "win32" ] }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "node_modules/@stylistic/eslint-plugin": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.2.0.tgz", @@ -1914,6 +1946,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", diff --git a/package.json b/package.json index a6b1d3c..2008f04 100644 --- a/package.json +++ b/package.json @@ -7,6 +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", "build": "NODE_ENV=production vite build --mode prod" }, "devDependencies": { @@ -17,6 +19,7 @@ "vite": "^6.3.1" }, "dependencies": { - "chart.js": "^4.4.9" + "chart.js": "^4.4.9", + "joi": "^17.13.3" } }