diff --git a/.gitignore b/.gitignore index 491fc35..f673eb0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +gen node_modules lib diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 0000000..103fc83 --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,3 @@ +semi: false +arrowParens: avoid +printWidth: 100 diff --git a/otjsmd.config.js b/otjsmd.config.js deleted file mode 100644 index bf037ae..0000000 --- a/otjsmd.config.js +++ /dev/null @@ -1,21 +0,0 @@ -// @ts-check -import { dirname, join } from "node:path" -import { fileURLToPath } from "node:url" -import { jsonSchema } from "optolith-tsjsonschemamd/renderers" - -const rootDir = dirname(fileURLToPath(import.meta.url)) -const sourceDir = join(rootDir, "src") -const schemaDir = join(rootDir, "schema") - -/** @type {import("optolith-tsjsonschemamd").GeneratorOptions} */ -export default { - sourceDir: sourceDir, - outputs: [ - { - targetDir: schemaDir, - renderer: jsonSchema({ spec: "Draft_07", allowAdditionalProperties: true }), - } - ], - clean: true, - verbose: false -} diff --git a/package-lock.json b/package-lock.json index ef98cab..f73048d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,29 @@ "version": "0.1.1", "license": "MPL-2.0", "devDependencies": { - "@optolith/ts-json-definition-transpiler": "^0.11.3", - "@types/node": "^22.13.8", - "typescript": "^5.8.2" + "@types/node": "^25.0.3", + "optolith-database-schema": "^0.26.2", + "tsondb": "^0.16.0", + "typescript": "^5.9.3" } }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true, + "license": "MIT" + }, "node_modules/@optolith/helpers": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@optolith/helpers/-/helpers-0.2.2.tgz", @@ -21,34 +39,998 @@ "dev": true, "license": "MPL-2.0" }, - "node_modules/@optolith/ts-json-definition-transpiler": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@optolith/ts-json-definition-transpiler/-/ts-json-definition-transpiler-0.11.3.tgz", - "integrity": "sha512-qnpnXOBmpvVR++rM0Ol/LNcb8qUkCzqj1kIOu/+Ven36rgd/yjtl1MKiXYCq9TDL2IGi4m29XfsQosBQkpfvuA==", + "node_modules/@preact/signals": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@preact/signals/-/signals-2.5.1.tgz", + "integrity": "sha512-VPjk5YFt7i11Fi4UK0tzaEe5xLwfhUxXL3l89ocxQ5aPz7bRo8M5+N73LjBMPklyXKYKz6YsNo4Smp8n6nplng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@preact/signals-core": "^1.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact": ">= 10.25.0 || >=11.0.0-0" + } + }, + "node_modules/@preact/signals-core": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.12.1.tgz", + "integrity": "sha512-BwbTXpj+9QutoZLQvbttRg5x3l5468qaV2kufh+51yha1c53ep5dY4kTuZR35+3pAZxpfQerGJiQqg34ZNZ6uA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@types/node": { + "version": "25.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz", + "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "license": "MIT", + "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/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/messageformat": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/messageformat/-/messageformat-4.0.0.tgz", + "integrity": "sha512-XKmJ/ffTWToWOlHJzt85ZChQgVGC0LHzNWuNK8zuYpNySsB0nIEmytOdSAOW9ETKtkajAUJf520m5gFHHnrTYg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19 || ^22.12 || >=24" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optolith-database-schema": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/optolith-database-schema/-/optolith-database-schema-0.26.2.tgz", + "integrity": "sha512-K9/vgSM9N1KtwJaDs12rpRfbv1BEaTZZqFHJwZXj6oHhZeU1ZFiSZt4YbTozlhDJ1ZOKuDfFqm52CbZ/V6lm+A==", "dev": true, "license": "MPL-2.0", "dependencies": { "@optolith/helpers": "^0.2.2", - "typescript": "^5.8.2" - }, - "bin": { - "otjsmd": "lib/bin/cli.js" + "tsondb": "^0.16.0", + "yaml": "^2.8.1" } }, - "node_modules/@types/node": { - "version": "22.13.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.8.tgz", - "integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==", + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/preact": { + "version": "10.28.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.1.tgz", + "integrity": "sha512-u1/ixq/lVQI0CakKNvLDEcW5zfCjUQfZdK9qqWuIJtsezuyG6pk9TWj75GMuI/EzRSZB/VAE43sNWWZfiy8psw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-iso": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/preact-iso/-/preact-iso-2.11.1.tgz", + "integrity": "sha512-rLy0RmzP/hrDjnFdnEblxFgKtzUj4njkHrpGJBGS7S4QuYw1zv0lA38qsWpeAAB10JAz/hF2CsHrLen9ufCtbw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "preact": ">=10 || >= 11.0.0-0", + "preact-render-to-string": ">=6.4.0" + } + }, + "node_modules/preact-render-to-string": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.6.4.tgz", + "integrity": "sha512-Bn6eQZ5SQ5loVEcC/mZmKT7HzO5Z/+vYzxfE/W2N468oSoNMJVdFGApF0GyXq0lDthuyXKTmtZ8k20NpYjr6Rw==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "preact": ">=10 || >= 11.0.0-0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.20.0" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-cli-args": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/simple-cli-args/-/simple-cli-args-0.1.3.tgz", + "integrity": "sha512-xDUf0CKojlwwYWtOwE8cr4D40o+LB/L2AN9qrnv763KiUWDE9bLzT0KwvDga5/lUnxKO57cPdm/8t0/RwGQKbA==", + "dev": true, + "license": "MPL-2.0" + }, + "node_modules/simple-git": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.30.0.tgz", + "integrity": "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsondb": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/tsondb/-/tsondb-0.16.0.tgz", + "integrity": "sha512-1Elof6C0ST09wzy9pmw9znv+yU5h6tayS6+uQLHLHLSSnYvjKmcjHjS9NJFLNiwHNrH6XSUKFi/vGoK6DzgcOA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@preact/signals": "^2.5.1", + "debug": "^4.4.3", + "express": "^5.1.0", + "messageformat": "^4.0.0", + "preact": "^10.27.2", + "preact-iso": "^2.11.0", + "simple-cli-args": "^0.1.3", + "simple-git": "^3.30.0" + }, + "bin": { + "tsondb": "dist/src/bin/tsondb.js" + }, + "engines": { + "node": ">=23.6.0" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" } }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -60,11 +1042,54 @@ } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } } } } diff --git a/package.json b/package.json index 447e42a..6119437 100644 --- a/package.json +++ b/package.json @@ -9,14 +9,14 @@ "CHANGELOG.md" ], "scripts": { - "build:ts": "tsc", - "watch:ts": "tsc -w", - "build:schema": "otjsmd", - "watch:schema": "otjsmd -w" + "build": "tsc", + "watch": "tsc -w", + "generate": "tsondb generate" }, "exports": { - ".": "./lib/character.js", - "./schema": "./schema/character.schema.json" + ".": "./lib/index.js", + "./gen": "./gen/types.d.ts", + "./gen/schema": "./gen/types.schema.json" }, "repository": { "type": "git", @@ -28,10 +28,13 @@ "url": "https://github.com/Optolith/character-schema/issues" }, "homepage": "https://github.com/Optolith/character-schema#readme", + "dependencies": { + "optolith-database-schema": "^0.26.2", + "tsondb": "^0.16.0" + }, "devDependencies": { - "@optolith/ts-json-definition-transpiler": "^0.11.3", - "@types/node": "^22.13.8", - "typescript": "^5.8.2" + "@types/node": "^25.0.3", + "typescript": "^5.9.3" }, "type": "module" } diff --git a/schema/character.schema.json b/schema/character.schema.json deleted file mode 100644 index 0f363e0..0000000 --- a/schema/character.schema.json +++ /dev/null @@ -1,1561 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft-07/schema", - "$id": "/character.schema.json", - "definitions": { - "Character": { - "title": "Character", - "description": "An exported character object from Optolith. Properties that are intended for external use only and thus are ignored when imported by Optolith are marked as read-only.", - "type": "object", - "properties": { - "version": { - "title": "Character Schema Version", - "description": "A valid semantic version (https://semver.org), representing the character schema version of this file.", - "type": "string", - "pattern": "^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$" - }, - "name": { - "title": "Name", - "description": "The character's name.", - "type": "string", - "minLength": 1 - }, - "date_created": { - "title": "Date of Creation", - "description": "Date of character creation, in ISO8601 format.", - "type": "string", - "format": "date-time" - }, - "date_last_modified": { - "title": "Date of Last Modification", - "description": "Date of character last modified, in ISO8601 format.", - "type": "string", - "format": "date-time" - }, - "adventure_points": { - "title": "Adventure Points", - "description": "An object storing all kinds of basic and calculated adventure points.", - "type": "object", - "properties": { - "total": { - "description": "Total adventure points.", - "type": "integer", - "minimum": 1 - }, - "available": { - "description": "Adventure points available.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent": { - "description": "Adventure points spent.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_advantages": { - "description": "Adventure points spent on advantages.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_magical_advantages": { - "description": "Adventure points spent on magical advantages.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_blessed_advantages": { - "description": "Adventure points spent on blessed advantages.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_disadvantages": { - "description": "Adventure points spent on disadvantages.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_magical_disadvantages": { - "description": "Adventure points spent on magical disadvantages.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_blessed_disadvantages": { - "description": "Adventure points spent on blessed disadvantages.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_race": { - "description": "Adventure points spent on race.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_attributes": { - "description": "Adventure points spent on attributes.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_skills": { - "description": "Adventure points spent on skills.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_combat_techniques": { - "description": "Adventure points spent on combat techniques.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_spells": { - "description": "Adventure points spent on spells, rituals and its enhancements.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_cantrips": { - "description": "Adventure points spent on cantrips.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_magical_actions": { - "description": "Adventure points spent on all kinds of magical actions.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_liturgical_chants": { - "description": "Adventure points spent on liturgical chant, ceremonies and its enhancements.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_blessings": { - "description": "Adventure points spent on blessings.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_special_abilities": { - "description": "Adventure points spent on all kinds of special abilities.", - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "spent_on_energies": { - "description": "Adventure points spent on adding and buying back energy points.", - "type": "integer", - "minimum": 0, - "readOnly": true - } - }, - "required": [ - "total", - "available", - "spent", - "spent_on_advantages", - "spent_on_magical_advantages", - "spent_on_blessed_advantages", - "spent_on_disadvantages", - "spent_on_magical_disadvantages", - "spent_on_blessed_disadvantages", - "spent_on_race", - "spent_on_attributes", - "spent_on_skills", - "spent_on_combat_techniques", - "spent_on_spells", - "spent_on_cantrips", - "spent_on_magical_actions", - "spent_on_liturgical_chants", - "spent_on_blessings", - "spent_on_special_abilities", - "spent_on_energies" - ], - "additionalProperties": true - }, - "experience_level": { - "title": "Experience Level Settings", - "description": "An object storing the identifiers of the experience level the character started at and the current experience level, derived from total adventure points.", - "type": "object", - "properties": { - "start_id": { - "description": "The start experience level identifier.", - "type": "integer", - "maximum": 7, - "minimum": 1 - }, - "current_id": { - "description": "The current experience level identifier.", - "type": "integer", - "maximum": 7, - "minimum": 1, - "readOnly": true - } - }, - "required": [ - "start_id", - "current_id" - ], - "additionalProperties": true - }, - "is_character_creation_finished": { - "title": "Character Creation Finished", - "description": "Describes whether the character creation is finished.", - "type": "boolean" - }, - "race": { - "title": "Race Settings", - "description": "An object storing the identifiers of the race and its optional race variant.", - "type": "object", - "properties": { - "id": { - "description": "The base race identifier.", - "type": "integer", - "minimum": 1 - }, - "variant_id": { - "description": "The race variant identifier.", - "type": "integer", - "minimum": 1 - }, - "selected_attribute_adjustment_id": { - "description": "The identifier of the attribute adjustment that has been selected from the race.", - "type": "integer", - "maximum": 8, - "minimum": 1 - } - }, - "required": [ - "id", - "selected_attribute_adjustment_id" - ], - "additionalProperties": true - }, - "culture": { - "title": "Culture Settings", - "description": "An object storing the identifier of the culture and if the cultural package has been applied.", - "type": "object", - "properties": { - "id": { - "description": "The culture identifier.", - "type": "integer", - "minimum": 1 - }, - "is_cultural_package_applied": { - "description": "Describes whether the cultural package has been applied when creating the character.", - "type": "boolean" - } - }, - "required": [ - "id", - "is_cultural_package_applied" - ], - "additionalProperties": true - }, - "profession": { - "title": "Profession Settings", - "description": "An object storing the identifiers of the profession, its instance and its optional profession variant.", - "type": "object", - "properties": { - "id": { - "description": "The base profession identifier.", - "type": "integer", - "minimum": 1 - }, - "instance_id": { - "description": "The profession instance identifier. Profession instances are versions of the same profession but slightly different values, such as in extension rule books existing professions might get an additional style special ability.", - "type": "integer", - "minimum": 1 - }, - "variant_id": { - "description": "The profession variant identifier.", - "type": "integer", - "minimum": 1 - }, - "custom_name": { - "description": "A custom name for the profession, if provided.", - "type": "string", - "minLength": 1 - } - }, - "required": [ - "id", - "instance_id" - ], - "additionalProperties": true - }, - "curriculum": { - "title": "Curriculum Settings", - "description": "An object storing the identifiers of the curriculum and it’s optional lesson package.", - "type": "object", - "properties": { - "educational_institution_id": { - "description": "The educational institution's curriculum identifier.", - "type": "integer", - "minimum": 1 - }, - "lesson_package_id": { - "description": "The lesson package identifier.", - "type": "integer", - "maximum": 2, - "minimum": 1 - } - }, - "required": [ - "educational_institution_id" - ], - "additionalProperties": true - }, - "rules": { - "title": "Rules Settings", - "description": "The rules settings for the character.", - "type": "object", - "properties": { - "include_all_publications": { - "description": "Whether the character makes use of all publications except for publications with adult content, which have to be specified explicitly using `include_publications`.", - "type": "boolean" - }, - "include_publications": { - "description": "Explicitly used publications. If `include_all_publications` is set to `true`, only affects publications that are not covered by `include_all_publications`.", - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "description": "The publication identifier.", - "type": "integer" - } - }, - "required": [ - "id" - ], - "additionalProperties": true - } - }, - "active_focus_rules": { - "description": "Active focus rules.", - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "description": "The focus rule identifier.", - "type": "integer" - } - }, - "required": [ - "id" - ], - "additionalProperties": true - } - }, - "active_optional_rules": { - "description": "Active optional rules.", - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "description": "The focus rule identifier.", - "type": "integer" - }, - "options": { - "description": "An array of one or more options. The exact meaning of each option varies based on the optional rule.", - "type": "array", - "items": { - "type": "number" - }, - "minItems": 1 - } - }, - "required": [ - "id" - ], - "additionalProperties": true - } - } - }, - "required": [ - "include_all_publications", - "include_publications", - "active_focus_rules", - "active_optional_rules" - ], - "additionalProperties": true - }, - "personal_data": { - "title": "Personal Data", - "description": "Personal data such as hair color and place of birth.", - "type": "object", - "properties": { - "sex": { - "description": "The character's sex. It does not have to be binary, although it always must be specified how to handle it in the context of binary sex prerequisites. You can also provide a custom sex with a custom name.", - "$ref": "#/definitions/Sex" - }, - "family": { - "description": "The family names and/or family members.", - "type": "string", - "minLength": 1 - }, - "place_of_birth": { - "description": "The place where the character was born.", - "type": "string", - "minLength": 1 - }, - "date_of_birth": { - "description": "The date when the character was born.", - "type": "string", - "minLength": 1 - }, - "age": { - "description": "The age of the character.", - "type": "string", - "minLength": 1 - }, - "hair_color": { - "description": "The hair color of the character.", - "$ref": "#/definitions/Color" - }, - "eye_color": { - "description": "The eye color of the character.", - "$ref": "#/definitions/Color" - }, - "size": { - "description": "The size of the character.", - "type": "string", - "minLength": 1 - }, - "weight": { - "description": "The weight of the character.", - "type": "string", - "minLength": 1 - }, - "title": { - "description": "The character's title(s).", - "type": "string", - "minLength": 1 - }, - "social_status_id": { - "description": "The social status identifier.", - "type": "integer", - "maximum": 5, - "minimum": 1 - }, - "characteristics": { - "description": "The character's characteristics.", - "type": "string", - "minLength": 1 - }, - "other_info": { - "description": "Other information about the character.", - "type": "string", - "minLength": 1 - } - }, - "required": [ - "sex" - ], - "additionalProperties": true - }, - "advantages": { - "title": "Advantages", - "$ref": "#/definitions/Activatables" - }, - "disadvantages": { - "title": "Disadvantages", - "$ref": "#/definitions/Activatables" - }, - "special_abilities": { - "title": "Special Abilities", - "description": "Lists of active special abilities, by group.", - "type": "object", - "properties": { - "advanced_combat_special_abilities": { - "title": "Advanced Combat Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "advanced_karma_special_abilities": { - "title": "Advanced Karma Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "advanced_magical_special_abilities": { - "title": "Advanced Magical Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "advanced_skill_special_abilities": { - "title": "Advanced Skill Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "ancestor_glyphs": { - "title": "Ancestor Glyphs", - "$ref": "#/definitions/Activatables" - }, - "arcane_orb_enchantments": { - "title": "Arcane Orb Enchantments", - "$ref": "#/definitions/Activatables" - }, - "attire_enchantments": { - "title": "Attire Enchantments", - "$ref": "#/definitions/Activatables" - }, - "blessed_traditions": { - "title": "Blessed Traditions", - "$ref": "#/definitions/Activatables" - }, - "bowl_enchantments": { - "title": "Bowl Enchantments", - "$ref": "#/definitions/Activatables" - }, - "brawling_special_abilities": { - "title": "Brawling Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "cauldron_enchantments": { - "title": "Cauldron Enchantments", - "$ref": "#/definitions/Activatables" - }, - "ceremonial_item_special_abilities": { - "title": "Ceremonial Item Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "chronicle_enchantments": { - "title": "Chronicle Enchantments", - "$ref": "#/definitions/Activatables" - }, - "combat_special_abilities": { - "title": "Combat Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "combat_style_special_abilities": { - "title": "Combat Style Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "command_special_abilities": { - "title": "Command Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "dagger_rituals": { - "title": "Dagger Rituals", - "$ref": "#/definitions/Activatables" - }, - "familiar_special_abilities": { - "title": "Familiar Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "fate_point_sex_special_abilities": { - "title": "Fate Point Sex Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "fate_point_special_abilities": { - "title": "Fate Point Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "fools_hat_enchantments": { - "title": "Fool’s Hat Enchantments", - "$ref": "#/definitions/Activatables" - }, - "general_special_abilities": { - "title": "General Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "instrument_enchantments": { - "title": "Instrument Enchantments", - "$ref": "#/definitions/Activatables" - }, - "karma_special_abilities": { - "title": "Karma Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "krallenkettenzauber": { - "title": "Krallenkettenzauber", - "$ref": "#/definitions/Activatables" - }, - "liturgical_style_special_abilities": { - "title": "Liturgical Style Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "lycantropic_gifts": { - "title": "Lycantropic Gifts", - "$ref": "#/definitions/Activatables" - }, - "magical_runes": { - "title": "Magical Runes", - "$ref": "#/definitions/Activatables" - }, - "magical_special_abilities": { - "title": "Magical Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "magical_traditions": { - "title": "Magical Traditions", - "$ref": "#/definitions/Activatables" - }, - "magic_style_special_abilities": { - "title": "Magic Style Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "orb_enchantments": { - "title": "Orb Enchantments", - "$ref": "#/definitions/Activatables" - }, - "pact_gifts": { - "title": "Pact Gifts", - "$ref": "#/definitions/Activatables" - }, - "protective_warding_circle_special_abilities": { - "title": "Protective/Warding Circle Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "ring_enchantments": { - "title": "Ring Enchantments", - "$ref": "#/definitions/Activatables" - }, - "sermons": { - "title": "Sermons", - "$ref": "#/definitions/Activatables" - }, - "sex_special_abilities": { - "title": "Sex Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "sickle_rituals": { - "title": "Sickle Rituals", - "$ref": "#/definitions/Activatables" - }, - "sikaryan_drain_special_abilities": { - "title": "Sikaryan Drain Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "skill_style_special_abilities": { - "title": "Skill Style Special Abilities", - "$ref": "#/definitions/Activatables" - }, - "spell_sword_enchantments": { - "title": "Spell-Sword Enchantments", - "$ref": "#/definitions/Activatables" - }, - "staff_enchantments": { - "title": "Staff Enchantments", - "$ref": "#/definitions/Activatables" - }, - "toy_enchantments": { - "title": "Toy Enchantments", - "$ref": "#/definitions/Activatables" - }, - "trinkhornzauber": { - "title": "Trinkhornzauber", - "$ref": "#/definitions/Activatables" - }, - "vampiric_gifts": { - "title": "Vampiric Gifts", - "$ref": "#/definitions/Activatables" - }, - "visions": { - "title": "Visions", - "$ref": "#/definitions/Activatables" - }, - "wand_enchantments": { - "title": "Wand Enchantments", - "$ref": "#/definitions/Activatables" - }, - "weapon_enchantments": { - "title": "Weapon Enchantments", - "$ref": "#/definitions/Activatables" - } - }, - "required": [], - "additionalProperties": true - }, - "attributes": { - "title": "Attributes", - "description": "A list of attributes with values above 8. Attributes that are not listed here default to 8.", - "type": "array", - "items": { - "$ref": "#/definitions/Attribute" - }, - "minItems": 1 - }, - "derived_characteristics": { - "title": "Derived Characteristics", - "description": "Computed, set and bought values for/of derived characteristics.", - "type": "object", - "properties": { - "life_points": { - "title": "Life Points", - "$ref": "#/definitions/Energy" - }, - "arcane_energy": { - "title": "Arcane Energy", - "description": "This property does not exist if the character does not have the active advantage Spellcaster and an active magical tradition special ability.", - "$ref": "#/definitions/EnergyWithBuyBack" - }, - "karma_points": { - "title": "Karma Points", - "description": "This property does not exist if the character does not have the active advantage Blessed One and an active blessed tradition special ability.", - "$ref": "#/definitions/EnergyWithBuyBack" - }, - "spirit": { - "title": "Spirit", - "$ref": "#/definitions/DerivedCharacteristic" - }, - "toughness": { - "title": "Toughness", - "$ref": "#/definitions/DerivedCharacteristic" - }, - "dodge": { - "title": "Dodge", - "$ref": "#/definitions/DerivedCharacteristic" - }, - "initiative": { - "title": "Initiative", - "$ref": "#/definitions/DerivedCharacteristic" - }, - "movement": { - "title": "Movement", - "$ref": "#/definitions/DerivedCharacteristic" - }, - "wound_threshold": { - "title": "Wound Threshold", - "description": "This property is not present if the required focus rules are not activated.", - "$ref": "#/definitions/DerivedCharacteristic" - } - }, - "required": [ - "life_points", - "spirit", - "toughness", - "dodge", - "initiative", - "movement", - "wound_threshold" - ], - "additionalProperties": true - }, - "skills": { - "title": "Skills", - "description": "A list of skills with values above 0. Skills that are not listed here default to 0.", - "type": "array", - "items": { - "$ref": "#/definitions/Skill" - } - }, - "combat_techniques": { - "title": "Combat Techniques", - "description": "Lists of combat techniques, by group.", - "type": "object", - "properties": { - "melee": { - "title": "Melee Combat Techniques", - "description": "A list of melee combat techniques with values above 6. Combat techniques that are not listed here default to 6.", - "type": "array", - "items": { - "$ref": "#/definitions/CombatTechnique" - } - }, - "ranged": { - "title": "Ranged Combat Techniques", - "description": "A list of ranged combat techniques with values above 6. Combat techniques that are not listed here default to 6.", - "type": "array", - "items": { - "$ref": "#/definitions/CombatTechnique" - } - } - }, - "required": [ - "melee", - "ranged" - ], - "additionalProperties": true - }, - "cantrips": { - "title": "Cantrips", - "description": "A list of purchased cantrips.", - "type": "array", - "items": { - "$ref": "#/definitions/TinyActivatable" - }, - "minItems": 1 - }, - "spells": { - "title": "Spells", - "description": "A list of active spells.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRatedWithEnhancements" - }, - "minItems": 1 - }, - "rituals": { - "title": "Rituals", - "description": "A list of active rituals.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRatedWithEnhancements" - }, - "minItems": 1 - }, - "magical_actions": { - "title": "Magical Actions", - "description": "Lists of magical actions, by group.", - "type": "object", - "properties": { - "curses": { - "title": "Curses", - "description": "A list of active curses.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRated" - }, - "minItems": 1 - }, - "elven_magical_songs": { - "title": "Elven Magical Songs", - "description": "A list of active Elven magical songs.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRated" - }, - "minItems": 1 - }, - "domination_rituals": { - "title": "Domination Rituals", - "description": "A list of active domination rituals.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRated" - }, - "minItems": 1 - }, - "magical_dances": { - "title": "Magical Dances", - "description": "A list of active magical dances.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRated" - }, - "minItems": 1 - }, - "magical_melodies": { - "title": "Magical Melodies", - "description": "A list of active magical melodies.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRated" - }, - "minItems": 1 - }, - "jester_tricks": { - "title": "Jester Tricks", - "description": "A list of active jester tricks.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRated" - }, - "minItems": 1 - }, - "animist_powers": { - "title": "Animist Powers", - "description": "A list of active animist powers.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRated" - }, - "minItems": 1 - }, - "geode_rituals": { - "title": "Geode Rituals", - "description": "A list of active geode rituals.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRated" - }, - "minItems": 1 - }, - "zibilja_rituals": { - "title": "Zibilja Rituals", - "description": "A list of active zibilja rituals.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRated" - }, - "minItems": 1 - } - }, - "required": [], - "minProperties": 1, - "additionalProperties": true - }, - "blessings": { - "title": "Blessings", - "description": "A list of purchased blessings.", - "type": "array", - "items": { - "$ref": "#/definitions/TinyActivatable" - }, - "minItems": 1 - }, - "liturgical_chants": { - "title": "Liturgical Chants", - "description": "A list of active liturgical chants.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRatedWithEnhancements" - }, - "minItems": 1 - }, - "ceremonies": { - "title": "Ceremonies", - "description": "A list of active ceremonies.", - "type": "array", - "items": { - "$ref": "#/definitions/ActivatableRatedWithEnhancements" - }, - "minItems": 1 - }, - "items": { - "type": "object", - "properties": {}, - "required": [], - "additionalProperties": true - }, - "hit_zone_armors": { - "type": "object", - "properties": {}, - "required": [], - "additionalProperties": true - }, - "purse": { - "description": "The money the character owns.", - "$ref": "#/definitions/Purse" - }, - "creatures": { - "type": "object", - "properties": {}, - "required": [], - "additionalProperties": true - }, - "pact": { - "type": "object", - "properties": {}, - "required": [], - "additionalProperties": true - } - }, - "required": [ - "version", - "name", - "date_created", - "date_last_modified", - "adventure_points", - "experience_level", - "is_character_creation_finished", - "race", - "culture", - "profession", - "rules", - "personal_data", - "advantages", - "disadvantages", - "special_abilities", - "attributes", - "derived_characteristics", - "skills", - "combat_techniques", - "magical_actions", - "items", - "purse" - ], - "additionalProperties": true - }, - "Sex": { - "description": "The character's sex. It does not have to be binary, although it always must be specified how to handle it in the context of binary sex prerequisites. You can also provide a custom sex with a custom name.", - "oneOf": [ - { - "$ref": "#/definitions/BinarySex" - }, - { - "$ref": "#/definitions/NonBinarySex" - }, - { - "$ref": "#/definitions/CustomSex" - } - ] - }, - "BinarySex": { - "description": "A binary sex option.", - "type": "object", - "properties": { - "type": { - "oneOf": [ - { - "const": "Male" - }, - { - "const": "Female" - } - ] - } - }, - "required": [ - "type" - ], - "additionalProperties": true - }, - "NonBinarySex": { - "description": "A non-binary sex option.", - "type": "object", - "properties": { - "type": { - "oneOf": [ - { - "const": "BalThani" - }, - { - "const": "Tsajana" - } - ] - }, - "binary_handling": { - "description": "Defines how a non-binary sex should be treated when checking prerequisites.", - "$ref": "#/definitions/BinaryHandling" - } - }, - "required": [ - "type", - "binary_handling" - ], - "additionalProperties": true - }, - "CustomSex": { - "description": "A custom non-binary sex option.", - "type": "object", - "properties": { - "type": { - "const": "Custom" - }, - "name": { - "description": "The custom sex name.", - "type": "string", - "minLength": 1 - }, - "binary_handling": { - "description": "Defines how a non-binary sex should be treated when checking prerequisites.", - "$ref": "#/definitions/BinaryHandling" - } - }, - "required": [ - "type", - "name", - "binary_handling" - ], - "additionalProperties": true - }, - "BinaryHandling": { - "description": "Defines how a non-binary sex should be treated when checking prerequisites.", - "type": "object", - "properties": { - "as_male": { - "description": "Defines if the sex should be treated as male when checking prerequisites.", - "type": "boolean" - }, - "as_female": { - "description": "Defines if the sex should be treated as female when checking prerequisites.", - "type": "boolean" - } - }, - "required": [ - "as_male", - "as_female" - ], - "additionalProperties": true - }, - "Color": { - "oneOf": [ - { - "$ref": "#/definitions/PredefinedColor" - }, - { - "$ref": "#/definitions/CustomColor" - } - ] - }, - "PredefinedColor": { - "description": "A predefined color.", - "type": "object", - "properties": { - "type": { - "const": "Predefined" - }, - "id": { - "description": "The color identifier.", - "type": "integer" - } - }, - "required": [ - "type", - "id" - ], - "additionalProperties": true - }, - "CustomColor": { - "description": "A custom color.", - "type": "object", - "properties": { - "type": { - "const": "Custom" - }, - "name": { - "description": "The custom color name.", - "type": "string", - "minLength": 1 - } - }, - "required": [ - "type", - "name" - ], - "additionalProperties": true - }, - "DerivedCharacteristic": { - "type": "object", - "properties": { - "base": { - "description": "The base value.", - "type": "integer", - "readOnly": true - }, - "modifier": { - "description": "The modifier depending on relevant advantages, disadvantages and special abilities.", - "type": "integer", - "readOnly": true - }, - "value": { - "description": "The (final) value.", - "type": "integer", - "readOnly": true - } - }, - "required": [ - "base", - "modifier", - "value" - ], - "additionalProperties": true - }, - "Energy": { - "type": "object", - "properties": { - "base": { - "description": "The base value.", - "type": "integer", - "readOnly": true - }, - "modifier": { - "description": "The modifier depending on relevant advantages, disadvantages and special abilities.", - "type": "integer", - "readOnly": true - }, - "purchased": { - "description": "The number of points purchased.", - "type": "integer", - "minimum": 0 - }, - "permanently_lost": { - "description": "The number of points permanently lost.", - "type": "integer", - "minimum": 0 - }, - "maximum": { - "description": "The (final) maximum.", - "type": "integer", - "readOnly": true - } - }, - "required": [ - "base", - "modifier", - "purchased", - "permanently_lost", - "maximum" - ], - "additionalProperties": true - }, - "EnergyWithBuyBack": { - "type": "object", - "properties": { - "base": { - "description": "The base value.", - "type": "integer", - "readOnly": true - }, - "modifier": { - "description": "The modifier depending on relevant advantages, disadvantages and special abilities.", - "type": "integer", - "readOnly": true - }, - "purchased": { - "description": "The number of points purchased.", - "type": "integer", - "minimum": 0 - }, - "permanently_lost": { - "description": "The number of points permanently lost.", - "type": "integer", - "minimum": 0 - }, - "permanently_lost_bought_back": { - "description": "The number of permanently lost points that have been bought back.", - "type": "integer", - "minimum": 0 - }, - "maximum": { - "description": "The (final) maximum.", - "type": "integer", - "readOnly": true - } - }, - "required": [ - "base", - "modifier", - "purchased", - "permanently_lost", - "permanently_lost_bought_back", - "maximum" - ], - "additionalProperties": true - }, - "Attribute": { - "type": "object", - "properties": { - "id": { - "description": "The attribute identifier.", - "type": "integer" - }, - "value": { - "description": "The attribute value.", - "type": "integer", - "minimum": 9 - } - }, - "required": [ - "id", - "value" - ], - "additionalProperties": true - }, - "Skill": { - "type": "object", - "properties": { - "id": { - "description": "The skill identifier.", - "type": "integer" - }, - "value": { - "description": "The skill rating.", - "type": "integer", - "minimum": 1 - } - }, - "required": [ - "id", - "value" - ], - "additionalProperties": true - }, - "CombatTechnique": { - "type": "object", - "properties": { - "id": { - "description": "The combat technique identifier.", - "type": "integer" - }, - "value": { - "description": "The combat technique rating.", - "type": "integer", - "minimum": 7 - } - }, - "required": [ - "id", - "value" - ], - "additionalProperties": true - }, - "ActivatableRated": { - "type": "object", - "properties": { - "id": { - "description": "The skill identifier.", - "type": "integer" - }, - "value": { - "description": "The skill rating.", - "type": "integer", - "minimum": 0 - } - }, - "required": [ - "id", - "value" - ], - "additionalProperties": true - }, - "ActivatableRatedWithEnhancements": { - "type": "object", - "properties": { - "id": { - "description": "The skill identifier.", - "type": "integer" - }, - "value": { - "description": "The skill rating.", - "type": "integer", - "minimum": 0 - }, - "enhancements": { - "description": "Purchased enhancements.", - "type": "array", - "items": { - "type": "number" - }, - "minItems": 1, - "uniqueItems": true - } - }, - "required": [ - "id", - "value" - ], - "additionalProperties": true - }, - "TinyActivatable": { - "type": "object", - "properties": { - "id": { - "description": "The identifier.", - "type": "integer" - } - }, - "required": [ - "id" - ], - "additionalProperties": true - }, - "Purse": { - "title": "Purse", - "description": "The money the character owns.", - "type": "object", - "properties": { - "kreutzers": { - "description": "The number of kreutzers the character owns.", - "type": "integer", - "minimum": 0 - }, - "halers": { - "description": "The number of halers the character owns.", - "type": "integer", - "minimum": 0 - }, - "silverthalers": { - "description": "The number of silverthalers the character owns.", - "type": "integer", - "minimum": 0 - }, - "ducats": { - "description": "The number of ducats the character owns.", - "type": "integer", - "minimum": 0 - } - }, - "required": [ - "kreutzers", - "halers", - "silverthalers", - "ducats" - ], - "additionalProperties": true - }, - "Activatables": { - "title": "Activatables", - "description": "A list of active activatables. Activatables are advantages, disadvantages and special abilities.", - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "description": "The activatable identifier.", - "type": "integer" - }, - "instances": { - "description": "One or multiple activations of the activatable.", - "type": "array", - "items": { - "type": "object", - "properties": { - "options": { - "description": "One or multiple options for the activatable. The meaning depends on the activatable.", - "type": "array", - "items": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Predefined" - }, - "id": { - "description": "An identifier referencing a different entry.", - "type": "object", - "properties": { - "type": { - "description": "The entry type or `\"Generic\"` if it references a select option local to the entry.", - "oneOf": [ - { - "const": "Generic" - }, - { - "const": "Blessing" - }, - { - "const": "Cantrip" - }, - { - "const": "TradeSecret" - }, - { - "const": "Script" - }, - { - "const": "AnimalShape" - }, - { - "const": "ArcaneBardTradition" - }, - { - "const": "ArcaneDancerTradition" - }, - { - "const": "SexPractice" - }, - { - "const": "Race" - }, - { - "const": "Culture" - }, - { - "const": "BlessedTradition" - }, - { - "const": "Element" - }, - { - "const": "Property" - }, - { - "const": "Aspect" - }, - { - "const": "Disease" - }, - { - "const": "Poison" - }, - { - "const": "Language" - }, - { - "const": "Skill" - }, - { - "const": "MeleeCombatTechnique" - }, - { - "const": "RangedCombatTechnique" - }, - { - "const": "LiturgicalChant" - }, - { - "const": "Ceremony" - }, - { - "const": "Spell" - }, - { - "const": "Ritual" - } - ] - }, - "value": { - "description": "The numeric identifier.", - "type": "integer" - } - }, - "required": [ - "type", - "value" - ], - "additionalProperties": true - } - }, - "required": [ - "type", - "id" - ], - "additionalProperties": true - }, - { - "type": "object", - "properties": { - "type": { - "const": "Custom" - }, - "value": { - "description": "A user-entered text.", - "type": "string", - "minLength": 1 - } - }, - "required": [ - "type", - "value" - ], - "additionalProperties": true - } - ] - }, - "minItems": 1 - }, - "level": { - "description": "The instance level (if the activatable has levels).", - "type": "integer", - "minimum": 1 - }, - "custom_adventure_points_value": { - "description": "If provided, a custom adventure points value has been set for this instance.", - "type": "integer", - "minimum": 0 - } - }, - "required": [], - "additionalProperties": true - } - } - }, - "required": [ - "id", - "instances" - ], - "additionalProperties": true - } - } - } -} diff --git a/src/activatables.ts b/src/activatables.ts new file mode 100644 index 0000000..0c1f7ab --- /dev/null +++ b/src/activatables.ts @@ -0,0 +1,89 @@ +import { SelectOptionIdentifier } from "optolith-database-schema/types/_IdentifierGroup" +import { + Array, + Enum, + EnumCase, + IncludeIdentifier, + Integer, + Object as Object_, + Optional, + Required, + String, + TypeAlias, +} from "tsondb/schema/def" +import { UUIDv4 } from "./utils.js" + +export const TinyActivatable = TypeAlias(import.meta.filename, { + name: "TinyActivatable", + comment: "A tiny activatable item reference.", + type: () => + Object_({ + id: Required({ + comment: "The identifier.", + type: IncludeIdentifier(UUIDv4), + }), + }), +}) + +export const Activatable = TypeAlias(import.meta.filename, { + name: "Activatable", + comment: + "An activatable item reference. Activatables are advantages, disadvantages and special abilities.", + type: () => + Object_({ + id: Required({ + comment: "The identifier.", + type: IncludeIdentifier(UUIDv4), + }), + }), +}) + +export const ActivatableInstance = TypeAlias(import.meta.filename, { + name: "ActivatableInstance", + comment: "An instance of an activatable item.", + type: () => + Object_({ + options: Optional({ + comment: + "One or multiple options for the activatable. The meaning depends on the activatable.", + type: Array(IncludeIdentifier(ActivatableInstanceOption), { minItems: 1 }), + }), + level: Optional({ + comment: "The instance level (if the activatable has levels).", + type: Integer({ minimum: 1 }), + }), + custom_adventure_points_value: Optional({ + comment: "If provided, a custom adventure points value has been set for this instance.", + type: Integer({ minimum: 0 }), + }), + }), +}) + +export const ActivatableInstanceOption = Enum(import.meta.filename, { + name: "ActivatableInstanceOption", + comment: "An activatable option, either predefined or custom.", + values: () => ({ + Predefined: EnumCase({ + comment: "A predefined option.", + type: IncludeIdentifier(ReferencedErasedSelectOptionIdentifier), + }), + Custom: EnumCase({ + comment: "A custom option.", + type: String({ minLength: 1 }), + }), + }), +}) + +const ReferencedErasedSelectOptionIdentifier = Enum(import.meta.filename, { + name: "ReferencedErasedSelectOptionIdentifier", + values: () => + Object.fromEntries( + Object.keys(SelectOptionIdentifier.type.value.values).map(name => [ + name, + EnumCase({ type: IncludeIdentifier(UUIDv4) }), + ]) + ) as Record< + keyof typeof SelectOptionIdentifier.type.value.values, + EnumCase> + >, +}) diff --git a/src/adventurePoints.ts b/src/adventurePoints.ts new file mode 100644 index 0000000..b0d8c04 --- /dev/null +++ b/src/adventurePoints.ts @@ -0,0 +1,92 @@ +import { Integer, Object, Required, TypeAlias } from "tsondb/schema/def" +import { READ_ONLY_NOTE } from "./utils.js" + +export const AdventurePoints = TypeAlias(import.meta.filename, { + name: "AdventurePoints", + comment: "An object storing all kinds of basic and calculated adventure points.", + type: () => + Object({ + total: Required({ + comment: "Total adventure points.", + type: Integer({ minimum: 1 }), + }), + available: Required({ + comment: "Adventure points available." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spent: Required({ + comment: "Adventure points spent." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnAdvantages: Required({ + comment: "Adventure points spent on advantages." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnMagicalAdvantages: Required({ + comment: "Adventure points spent on magical advantages." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnBlessedAdvantages: Required({ + comment: "Adventure points spent on blessed advantages." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnDisadvantages: Required({ + comment: "Adventure points spent on disadvantages." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnMagicalAisadvantages: Required({ + comment: "Adventure points spent on magical disadvantages." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnBlessedDisadvantages: Required({ + comment: "Adventure points spent on blessed disadvantages." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnRace: Required({ + comment: "Adventure points spent on race." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnAttributes: Required({ + comment: "Adventure points spent on attributes." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnSkills: Required({ + comment: "Adventure points spent on skills." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnCombatTechniques: Required({ + comment: "Adventure points spent on combat techniques." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnSpells: Required({ + comment: "Adventure points spent on spells, rituals and its enhancements." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnCantrips: Required({ + comment: "Adventure points spent on cantrips." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnMagicalActions: Required({ + comment: "Adventure points spent on all kinds of magical actions." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnMiturgicalChants: Required({ + comment: + "Adventure points spent on liturgical chant, ceremonies and its enhancements." + + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnBlessings: Required({ + comment: "Adventure points spent on blessings." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnSpecialAbilities: Required({ + comment: "Adventure points spent on all kinds of special abilities." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + spentOnEnergies: Required({ + comment: "Adventure points spent on adding and buying back energy points." + READ_ONLY_NOTE, + type: Integer({ minimum: 0 }), + }), + }), +}) diff --git a/src/belongings.ts b/src/belongings.ts new file mode 100644 index 0000000..ca11fd7 --- /dev/null +++ b/src/belongings.ts @@ -0,0 +1,25 @@ +import { Integer, Object, Required, TypeAlias } from "tsondb/schema/def" + +export const Purse = TypeAlias(import.meta.filename, { + name: "Purse", + comment: "The money the character owns.", + type: () => + Object({ + kreutzers: Required({ + comment: "The number of kreutzers the character owns.", + type: Integer({ minimum: 0 }), + }), + halers: Required({ + comment: "The number of halers the character owns.", + type: Integer({ minimum: 0 }), + }), + silverthalers: Required({ + comment: "The number of silverthalers the character owns.", + type: Integer({ minimum: 0 }), + }), + ducats: Required({ + comment: "The number of ducats the character owns.", + type: Integer({ minimum: 0 }), + }), + }), +}) diff --git a/src/character.ts b/src/character.ts index ed156d2..41951ac 100644 --- a/src/character.ts +++ b/src/character.ts @@ -1,1358 +1,237 @@ -/** - * An exported character object from Optolith. Properties that are intended for external use only and thus are ignored when imported by Optolith are marked as read-only. - * @title Character - */ -export type Character = { - /** - * A valid semantic version (https://semver.org), representing the character schema version of this file. - * @title Character Schema Version - * @pattern ^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$ - */ - version: string - // The pattern is the recommended regex for semantic versioning: - // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string. - - /** - * The character's name. - * @title Name - * @minLength 1 - */ - name: string - - /** - * Date of character creation, in ISO8601 format. - * @title Date of Creation - * @format date-time - */ - date_created: string - - /** - * Date of character last modified, in ISO8601 format. - * @title Date of Last Modification - * @format date-time - */ - date_last_modified: string - - /** - * An object storing all kinds of basic and calculated adventure points. - * @title Adventure Points - */ - adventure_points: { - /** - * Total adventure points. - * @minimum 1 - * @integer - */ - total: number - - /** - * Adventure points available. - * @minimum 0 - * @integer - */ - readonly available: number - - /** - * Adventure points spent. - * @minimum 0 - * @integer - */ - readonly spent: number - - /** - * Adventure points spent on advantages. - * @minimum 0 - * @integer - */ - readonly spent_on_advantages: number - - /** - * Adventure points spent on magical advantages. - * @minimum 0 - * @integer - */ - readonly spent_on_magical_advantages: number - - /** - * Adventure points spent on blessed advantages. - * @minimum 0 - * @integer - */ - readonly spent_on_blessed_advantages: number - - /** - * Adventure points spent on disadvantages. - * @minimum 0 - * @integer - */ - readonly spent_on_disadvantages: number - - /** - * Adventure points spent on magical disadvantages. - * @minimum 0 - * @integer - */ - readonly spent_on_magical_disadvantages: number - - /** - * Adventure points spent on blessed disadvantages. - * @minimum 0 - * @integer - */ - readonly spent_on_blessed_disadvantages: number - - /** - * Adventure points spent on race. - * @minimum 0 - * @integer - */ - readonly spent_on_race: number - - /** - * Adventure points spent on attributes. - * @minimum 0 - * @integer - */ - readonly spent_on_attributes: number - - /** - * Adventure points spent on skills. - * @minimum 0 - * @integer - */ - readonly spent_on_skills: number - - /** - * Adventure points spent on combat techniques. - * @minimum 0 - * @integer - */ - readonly spent_on_combat_techniques: number - - /** - * Adventure points spent on spells, rituals and its enhancements. - * @minimum 0 - * @integer - */ - readonly spent_on_spells: number - - /** - * Adventure points spent on cantrips. - * @minimum 0 - * @integer - */ - readonly spent_on_cantrips: number - - /** - * Adventure points spent on all kinds of magical actions. - * @minimum 0 - * @integer - */ - readonly spent_on_magical_actions: number - - /** - * Adventure points spent on liturgical chant, ceremonies and its enhancements. - * @minimum 0 - * @integer - */ - readonly spent_on_liturgical_chants: number - - /** - * Adventure points spent on blessings. - * @minimum 0 - * @integer - */ - readonly spent_on_blessings: number - - /** - * Adventure points spent on all kinds of special abilities. - * @minimum 0 - * @integer - */ - readonly spent_on_special_abilities: number - - /** - * Adventure points spent on adding and buying back energy points. - * @minimum 0 - * @integer - */ - readonly spent_on_energies: number - } - - /** - * An object storing the identifiers of the experience level the character started at and the current experience level, derived from total adventure points. - * @title Experience Level Settings - */ - experience_level: { - /** - * The start experience level identifier. - * @minimum 1 - * @maximum 7 - * @integer - */ - start_id: number - - /** - * The current experience level identifier. - * @minimum 1 - * @maximum 7 - * @integer - */ - readonly current_id: number - } - - /** - * Describes whether the character creation is finished. - * @title Character Creation Finished - */ - is_character_creation_finished: boolean - - /** - * An object storing the identifiers of the race and its optional race variant. - * @title Race Settings - */ - race: { - /** - * The base race identifier. - * @minimum 1 - * @integer - */ - id: number - - /** - * The race variant identifier. - * @minimum 1 - * @integer - */ - variant_id?: number - - /** - * The identifier of the attribute adjustment that has been selected from the race. - * @minimum 1 - * @maximum 8 - * @integer - */ - selected_attribute_adjustment_id: number - } - - /** - * An object storing the identifier of the culture and if the cultural package has been applied. - * @title Culture Settings - */ - culture: { - /** - * The culture identifier. - * @minimum 1 - * @integer - */ - id: number - - /** - * Describes whether the cultural package has been applied when creating the character. - */ - is_cultural_package_applied: boolean - } - - /** - * An object storing the identifiers of the profession, its instance and its optional profession variant. - * @title Profession Settings - */ - profession: { - /** - * The base profession identifier. - * @minimum 1 - * @integer - */ - id: number - - /** - * The profession instance identifier. Profession instances are versions of the same profession but slightly different values, such as in extension rule books existing professions might get an additional style special ability. - * @minimum 1 - * @integer - */ - instance_id: number - - /** - * The profession variant identifier. - * @minimum 1 - * @integer - */ - variant_id?: number - - /** - * A custom name for the profession, if provided. - * @minLength 1 - */ - custom_name?: string - } - - /** - * An object storing the identifiers of the curriculum and it’s optional lesson package. - * @title Curriculum Settings - */ - curriculum?: { - /** - * The educational institution's curriculum identifier. - * @minimum 1 - * @integer - */ - educational_institution_id: number - - /** - * The lesson package identifier. - * @minimum 1 - * @maximum 2 - * @integer - */ - lesson_package_id?: number - } - - /** - * The rules settings for the character. - * @title Rules Settings - */ - rules: { - /** - * Whether the character makes use of all publications except for publications with adult content, which have to be specified explicitly using `include_publications`. - */ - include_all_publications: boolean - - /** - * Explicitly used publications. If `include_all_publications` is set to `true`, only affects publications that are not covered by `include_all_publications`. - */ - include_publications: { - /** - * The publication identifier. - * @integer - */ - id: number - }[] - - /** - * Active focus rules. - */ - active_focus_rules: { - /** - * The focus rule identifier. - * @integer - */ - id: number - }[] - - /** - * Active optional rules. - * @minimum 1 - * @integer - */ - active_optional_rules: { - /** - * The focus rule identifier. - * @integer - */ - id: number - - /** - * An array of one or more options. The exact meaning of each option varies based on the optional rule. - * @minItems 1 - */ - options?: (/** @integer @minimum 1 */ number)[] - }[] - } - - /** - * Personal data such as hair color and place of birth. - * @title Personal Data - */ - personal_data: { - /** - * The character's sex. It does not have to be binary, although it always must be specified how to handle it in the context of binary sex prerequisites. You can also provide a custom sex with a custom name. - */ - sex: Sex - - /** - * The family names and/or family members. - * @minLength 1 - */ - family?: string - - /** - * The place where the character was born. - * @minLength 1 - */ - place_of_birth?: string - - /** - * The date when the character was born. - * @minLength 1 - */ - date_of_birth?: string - - /** - * The age of the character. - * @minLength 1 - */ - age?: string - - /** - * The hair color of the character. - */ - hair_color?: Color - - /** - * The eye color of the character. - */ - eye_color?: Color - - /** - * The size of the character. - * @minLength 1 - */ - size?: string - - /** - * The weight of the character. - * @minLength 1 - */ - weight?: string - - /** - * The character's title(s). - * @minLength 1 - */ - title?: string - - /** - * The social status identifier. - * @minimum 1 - * @maximum 5 - * @integer - */ - social_status_id?: number - - /** - * The character's characteristics. - * @minLength 1 - */ - characteristics?: string - - /** - * Other information about the character. - * @minLength 1 - */ - other_info?: string - } - - /** - * @title Advantages - */ - advantages: Activatables - - /** - * @title Disadvantages - */ - disadvantages: Activatables - - /** - * Lists of active special abilities, by group. - * @title Special Abilities - */ - special_abilities: { - /** - * @title Advanced Combat Special Abilities - * @minItems 1 - */ - advanced_combat_special_abilities?: Activatables - - /** - * @title Advanced Karma Special Abilities - * @minItems 1 - */ - advanced_karma_special_abilities?: Activatables - - /** - * @title Advanced Magical Special Abilities - * @minItems 1 - */ - advanced_magical_special_abilities?: Activatables - - /** - * @title Advanced Skill Special Abilities - * @minItems 1 - */ - advanced_skill_special_abilities?: Activatables - - /** - * @title Ancestor Glyphs - * @minItems 1 - */ - ancestor_glyphs?: Activatables - - /** - * @title Arcane Orb Enchantments - * @minItems 1 - */ - arcane_orb_enchantments?: Activatables - - /** - * @title Attire Enchantments - * @minItems 1 - */ - attire_enchantments?: Activatables - - /** - * @title Blessed Traditions - * @minItems 1 - */ - blessed_traditions?: Activatables - - /** - * @title Bowl Enchantments - * @minItems 1 - */ - bowl_enchantments?: Activatables - - /** - * @title Brawling Special Abilities - * @minItems 1 - */ - brawling_special_abilities?: Activatables - - /** - * @title Cauldron Enchantments - * @minItems 1 - */ - cauldron_enchantments?: Activatables - - /** - * @title Ceremonial Item Special Abilities - * @minItems 1 - */ - ceremonial_item_special_abilities?: Activatables - - /** - * @title Chronicle Enchantments - * @minItems 1 - */ - chronicle_enchantments?: Activatables - - /** - * @title Combat Special Abilities - * @minItems 1 - */ - combat_special_abilities?: Activatables - - /** - * @title Combat Style Special Abilities - * @minItems 1 - */ - combat_style_special_abilities?: Activatables - - /** - * @title Command Special Abilities - * @minItems 1 - */ - command_special_abilities?: Activatables - - /** - * @title Dagger Rituals - * @minItems 1 - */ - dagger_rituals?: Activatables - - /** - * @title Familiar Special Abilities - * @minItems 1 - */ - familiar_special_abilities?: Activatables - - /** - * @title Fate Point Sex Special Abilities - * @minItems 1 - */ - fate_point_sex_special_abilities?: Activatables - - /** - * @title Fate Point Special Abilities - * @minItems 1 - */ - fate_point_special_abilities?: Activatables - - /** - * @title Fool’s Hat Enchantments - * @minItems 1 - */ - fools_hat_enchantments?: Activatables - - /** - * @title General Special Abilities - * @minItems 1 - */ - general_special_abilities?: Activatables - - /** - * @title Instrument Enchantments - * @minItems 1 - */ - instrument_enchantments?: Activatables - - /** - * @title Karma Special Abilities - * @minItems 1 - */ - karma_special_abilities?: Activatables - - /** - * @title Krallenkettenzauber - * @minItems 1 - */ - krallenkettenzauber?: Activatables - - /** - * @title Liturgical Style Special Abilities - * @minItems 1 - */ - liturgical_style_special_abilities?: Activatables - - /** - * @title Lycantropic Gifts - * @minItems 1 - */ - lycantropic_gifts?: Activatables - - /** - * @title Magical Runes - * @minItems 1 - */ - magical_runes?: Activatables - - /** - * @title Magical Special Abilities - * @minItems 1 - */ - magical_special_abilities?: Activatables - - /** - * @title Magical Traditions - * @minItems 1 - */ - magical_traditions?: Activatables - - /** - * @title Magic Style Special Abilities - * @minItems 1 - */ - magic_style_special_abilities?: Activatables - - /** - * @title Orb Enchantments - * @minItems 1 - */ - orb_enchantments?: Activatables - - /** - * @title Pact Gifts - * @minItems 1 - */ - pact_gifts?: Activatables - - /** - * @title Protective/Warding Circle Special Abilities - * @minItems 1 - */ - protective_warding_circle_special_abilities?: Activatables - - /** - * @title Ring Enchantments - * @minItems 1 - */ - ring_enchantments?: Activatables - - /** - * @title Sermons - * @minItems 1 - */ - sermons?: Activatables - - /** - * @title Sex Special Abilities - * @minItems 1 - */ - sex_special_abilities?: Activatables - - /** - * @title Sickle Rituals - * @minItems 1 - */ - sickle_rituals?: Activatables - - /** - * @title Sikaryan Drain Special Abilities - * @minItems 1 - */ - sikaryan_drain_special_abilities?: Activatables - - /** - * @title Skill Style Special Abilities - * @minItems 1 - */ - skill_style_special_abilities?: Activatables - - /** - * @title Spell-Sword Enchantments - * @minItems 1 - */ - spell_sword_enchantments?: Activatables - - /** - * @title Staff Enchantments - * @minItems 1 - */ - staff_enchantments?: Activatables - - /** - * @title Toy Enchantments - * @minItems 1 - */ - toy_enchantments?: Activatables - - /** - * @title Trinkhornzauber - * @minItems 1 - */ - trinkhornzauber?: Activatables - - /** - * @title Vampiric Gifts - * @minItems 1 - */ - vampiric_gifts?: Activatables - - /** - * @title Visions - * @minItems 1 - */ - visions?: Activatables - - /** - * @title Wand Enchantments - * @minItems 1 - */ - wand_enchantments?: Activatables - - /** - * @title Weapon Enchantments - * @minItems 1 - */ - weapon_enchantments?: Activatables - } - - /** - * A list of attributes with values above 8. Attributes that are not listed here default to 8. - * @title Attributes - * @minItems 1 - */ - attributes: Attribute[] - - /** - * Computed, set and bought values for/of derived characteristics. - * @title Derived Characteristics - */ - derived_characteristics: { - /** - * @title Life Points - */ - life_points: Energy - - /** - * This property does not exist if the character does not have the active advantage Spellcaster and an active magical tradition special ability. - * @title Arcane Energy - */ - arcane_energy?: EnergyWithBuyBack - - /** - * This property does not exist if the character does not have the active advantage Blessed One and an active blessed tradition special ability. - * @title Karma Points - */ - karma_points?: EnergyWithBuyBack - - /** - * @title Spirit - */ - spirit: DerivedCharacteristic - - /** - * @title Toughness - */ - toughness: DerivedCharacteristic - - /** - * @title Dodge - */ - dodge: DerivedCharacteristic - - /** - * @title Initiative - */ - initiative: DerivedCharacteristic - - /** - * @title Movement - */ - movement: DerivedCharacteristic - - /** - * This property is not present if the required focus rules are not activated. - * @title Wound Threshold - */ - wound_threshold: DerivedCharacteristic - } - - /** - * A list of skills with values above 0. Skills that are not listed here default to 0. - * @title Skills - */ - skills: Skill[] - - /** - * Lists of combat techniques, by group. - * @title Combat Techniques - */ - combat_techniques: { - /** - * A list of melee combat techniques with values above 6. Combat techniques that are not listed here default to 6. - * @title Melee Combat Techniques - */ - melee: CombatTechnique[] - - /** - * A list of ranged combat techniques with values above 6. Combat techniques that are not listed here default to 6. - * @title Ranged Combat Techniques - */ - ranged: CombatTechnique[] - } - - /** - * A list of purchased cantrips. - * @title Cantrips - * @minItems 1 - */ - cantrips?: TinyActivatable[] - - /** - * A list of active spells. - * @title Spells - * @minItems 1 - */ - spells?: ActivatableRatedWithEnhancements[] - - /** - * A list of active rituals. - * @title Rituals - * @minItems 1 - */ - rituals?: ActivatableRatedWithEnhancements[] - - /** - * Lists of magical actions, by group. - * @title Magical Actions - * @minProperties 1 - */ - magical_actions: { - /** - * A list of active curses. - * @title Curses - * @minItems 1 - */ - curses?: ActivatableRated[] - - /** - * A list of active Elven magical songs. - * @title Elven Magical Songs - * @minItems 1 - */ - elven_magical_songs?: ActivatableRated[] - - /** - * A list of active domination rituals. - * @title Domination Rituals - * @minItems 1 - */ - domination_rituals?: ActivatableRated[] - - /** - * A list of active magical dances. - * @title Magical Dances - * @minItems 1 - */ - magical_dances?: ActivatableRated[] - - /** - * A list of active magical melodies. - * @title Magical Melodies - * @minItems 1 - */ - magical_melodies?: ActivatableRated[] - - /** - * A list of active jester tricks. - * @title Jester Tricks - * @minItems 1 - */ - jester_tricks?: ActivatableRated[] - - /** - * A list of active animist powers. - * @title Animist Powers - * @minItems 1 - */ - animist_powers?: ActivatableRated[] - - /** - * A list of active geode rituals. - * @title Geode Rituals - * @minItems 1 - */ - geode_rituals?: ActivatableRated[] - - /** - * A list of active zibilja rituals. - * @title Zibilja Rituals - * @minItems 1 - */ - zibilja_rituals?: ActivatableRated[] - } - - /** - * A list of purchased blessings. - * @title Blessings - * @minItems 1 - */ - blessings?: TinyActivatable[] - - /** - * A list of active liturgical chants. - * @title Liturgical Chants - * @minItems 1 - */ - liturgical_chants?: ActivatableRatedWithEnhancements[] - - /** - * A list of active ceremonies. - * @title Ceremonies - * @minItems 1 - */ - ceremonies?: ActivatableRatedWithEnhancements[] - - items: {} - - hit_zone_armors?: {} - - /** - * The money the character owns. - */ - purse: Purse - - creatures?: {} - - pact?: {} -} - -/** - * The character's sex. It does not have to be binary, although it always must be specified how to handle it in the context of binary sex prerequisites. You can also provide a custom sex with a custom name. - */ -export type Sex = - | BinarySex - | NonBinarySex - | CustomSex - -/** - * A binary sex option. - */ -export type BinarySex = { - type: "Male" | "Female" -} - -/** - * A non-binary sex option. - */ -export type NonBinarySex ={ - type: "BalThani" | "Tsajana" - - /** - * Defines how a non-binary sex should be treated when checking prerequisites. - */ - binary_handling: BinaryHandling -} - -/** - * A custom non-binary sex option. - */ -export type CustomSex = { - type: "Custom" - - /** - * The custom sex name. - * @minLength 1 - */ - name: string - - /** - * Defines how a non-binary sex should be treated when checking prerequisites. - */ - binary_handling: BinaryHandling -} - -/** - * Defines how a non-binary sex should be treated when checking prerequisites. - */ -export type BinaryHandling = { - /** - * Defines if the sex should be treated as male when checking prerequisites. - */ - as_male: boolean - - /** - * Defines if the sex should be treated as female when checking prerequisites. - */ - as_female: boolean -} - -export type Color = - | PredefinedColor - | CustomColor - -/** - * A predefined color. - */ -export type PredefinedColor = { - type: "Predefined" - - /** - * The color identifier. - * @integer - */ - id: number -} - -/** - * A custom color. - */ -export type CustomColor = { - type: "Custom" - - /** - * The custom color name. - * @minLength 1 - */ - name: string -} - -export type DerivedCharacteristic = { - /** - * The base value. - * @integer - */ - readonly base: number - - /** - * The modifier depending on relevant advantages, disadvantages and special abilities. - * @integer - */ - readonly modifier: number - - /** - * The (final) value. - * @integer - */ - readonly value: number -} - -export type Energy = { - /** - * The base value. - * @integer - */ - readonly base: number - - /** - * The modifier depending on relevant advantages, disadvantages and special abilities. - * @integer - */ - readonly modifier: number - - /** - * The number of points purchased. - * @minimum 0 - * @integer - */ - purchased: number - - /** - * The number of points permanently lost. - * @integer - * @minimum 0 - */ - permanently_lost: number - - /** - * The (final) maximum. - * @integer - */ - readonly maximum: number -} - -export type EnergyWithBuyBack = { - /** - * The base value. - * @integer - */ - readonly base: number - - /** - * The modifier depending on relevant advantages, disadvantages and special abilities. - * @integer - */ - readonly modifier: number - - /** - * The number of points purchased. - * @minimum 0 - * @integer - */ - purchased: number - - /** - * The number of points permanently lost. - * @integer - * @minimum 0 - */ - permanently_lost: number - - /** - * The number of permanently lost points that have been bought back. - * @integer - * @minimum 0 - */ - permanently_lost_bought_back: number - - /** - * The (final) maximum. - * @integer - */ - readonly maximum: number -} - -export type Attribute = { - /** - * The attribute identifier. - * @integer - */ - id: number - - /** - * The attribute value. - * @minimum 9 - * @integer - */ - value: number -} - -export type Skill = { - /** - * The skill identifier. - * @integer - */ - id: number - - /** - * The skill rating. - * @minimum 1 - * @integer - */ - value: number -} - -export type CombatTechnique = { - /** - * The combat technique identifier. - * @integer - */ - id: number - - /** - * The combat technique rating. - * @minimum 7 - * @integer - */ - value: number -} - -export type ActivatableRated = { - /** - * The skill identifier. - * @integer - */ - id: number - - /** - * The skill rating. - * @minimum 0 - * @integer - */ - value: number -} - -export type ActivatableRatedWithEnhancements = { - /** - * The skill identifier. - * @integer - */ - id: number - - /** - * The skill rating. - * @minimum 0 - * @integer - */ - value: number - - /** - * Purchased enhancements. - * @minItems 1 - * @uniqueItems true - */ - enhancements?: (/** @integer @minimum 1 */number)[] -} - -export type TinyActivatable = { - /** - * The identifier. - * @integer - */ - id: number -} - -/** - * The money the character owns. - * @title Purse - */ -export type Purse = { - /** - * The number of kreutzers the character owns. - * @minimum 0 - * @integer - */ - kreutzers: number - - /** - * The number of halers the character owns. - * @minimum 0 - * @integer - */ - halers: number - - /** - * The number of silverthalers the character owns. - * @minimum 0 - * @integer - */ - silverthalers: number - - /** - * The number of ducats the character owns. - * @minimum 0 - * @integer - */ - ducats: number -} - -/** - * A list of active activatables. Activatables are advantages, disadvantages and special abilities. - * @title Activatables - */ -export type Activatables = { - /** - * The activatable identifier. - * @integer - */ - id: number - - /** - * One or multiple activations of the activatable. - */ - instances: { - /** - * One or multiple options for the activatable. The meaning depends on the activatable. - * @minItems 1 - */ - options?: ( - | { - type: "Predefined" - - /** - * An identifier referencing a different entry. - */ - id: { - /** - * The entry type or `"Generic"` if it references a select option local to the entry. - */ - type: - | "Generic" - | "Blessing" - | "Cantrip" - | "TradeSecret" - | "Script" - | "AnimalShape" - | "ArcaneBardTradition" - | "ArcaneDancerTradition" - | "SexPractice" - | "Race" - | "Culture" - | "BlessedTradition" - | "Element" - | "Property" - | "Aspect" - | "Disease" - | "Poison" - | "Language" - | "Skill" - | "MeleeCombatTechnique" - | "RangedCombatTechnique" - | "LiturgicalChant" - | "Ceremony" - | "Spell" - | "Ritual" - - /** - * The numeric identifier. - * @integer - */ - value: number - } - } - | { - type: "Custom" - - /** - * A user-entered text. - * @minLength 1 - */ - value: string - } - )[] - - /** - * The instance level (if the activatable has levels). - * @minimum 1 - * @integer - */ - level?: number - - /** - * If provided, a custom adventure points value has been set for this instance. - * @minimum 0 - * @integer - */ - custom_adventure_points_value?: number - }[] -}[] +import { + MagicalActionIdentifier, + SpecialAbilityIdentifier, +} from "optolith-database-schema/types/_IdentifierGroup" +import { + Array, + Boolean, + Date, + Entity, + IncludeIdentifier, + Object as Object_, + Optional, + Required, + String, + type MemberDecl, +} from "tsondb/schema/def" +import { Activatable, TinyActivatable } from "./activatables.js" +import { AdventurePoints } from "./adventurePoints.js" +import { Purse } from "./belongings.js" +import { Culture, Curriculum, ExperienceLevel, Profession, Race } from "./creation.js" +import { DerivedCharacteristic, Energy, EnergyWithBuyBack } from "./derivedCharacteristics.js" +import { PersonalData } from "./personalData.js" +import { + ActivatableRated, + ActivatableRatedWithEnhancements, + Attribute, + CombatTechnique, + Skill, +} from "./rated.js" +import { Rules } from "./rules.js" + +export const Character = Entity(import.meta.filename, { + name: "Character", + namePlural: "Characters", + comment: "An exported character object from Optolith.", + type: () => + Object_({ + version: Required({ + comment: + "A valid semantic version (https://semver.org), representing the character schema version of this file.", + type: String({ + pattern: + /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$/, + }), // The pattern is the recommended regex for semantic versioning: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string. + }), + name: Required({ + comment: "The character’s name.", + type: String({ minLength: 1 }), + }), + dateCreated: Required({ + comment: "Date of character creation, in ISO8601 format.", + type: Date({ time: true }), + }), + dateLastModified: Required({ + comment: "Date of character last modified, in ISO8601 format.", + type: Date({ time: true }), + }), + adventurePoints: Required({ + comment: "An object storing all kinds of basic and calculated adventure points.", + type: IncludeIdentifier(AdventurePoints), + }), + experienceLevel: Required({ + comment: + "An object storing the identifiers of the experience level the character started at and the current experience level, derived from total adventure points.", + type: IncludeIdentifier(ExperienceLevel), + }), + isCharacterCreationFinished: Required({ + comment: "Describes whether the character creation is finished.", + type: Boolean(), + }), + race: Required({ + comment: "An object storing the identifiers of the race and its optional race variant.", + type: IncludeIdentifier(Race), + }), + culture: Required({ + comment: + "An object storing the identifier of the culture and if the cultural package has been applied.", + type: IncludeIdentifier(Culture), + }), + profession: Required({ + comment: + "An object storing the identifiers of the profession, its instance and its optional profession variant.", + type: IncludeIdentifier(Profession), + }), + curriculum: Optional({ + comment: + "An object storing the identifiers of the curriculum and it’s optional lesson package.", + type: IncludeIdentifier(Curriculum), + }), + rules: Required({ + comment: "The rules settings for the character.", + type: IncludeIdentifier(Rules), + }), + personalData: Required({ + comment: "Personal data such as hair color and place of birth.", + type: IncludeIdentifier(PersonalData), + }), + advantages: Required({ + comment: "A list of active advantages.", + type: Array(IncludeIdentifier(Activatable), { minItems: 1 }), + }), + disadvantages: Required({ + comment: "A list of active disadvantages.", + type: Array(IncludeIdentifier(Activatable), { minItems: 1 }), + }), + specialAbilities: Required({ + comment: + "Lists of active special abilities, by group.\n\nThis object may grow if new special ability groups are introduced, which is not considered a breaking change.", + type: Object_( + Object.fromEntries( + Object.values(SpecialAbilityIdentifier.type.value.values).map(caseRef => [ + caseRef.type.entity.namePlural.slice(0, 1).toLowerCase() + + caseRef.type.entity.namePlural.slice(1), + Optional({ type: Array(IncludeIdentifier(Activatable), { minItems: 1 }) }), + ]) + ) as Record>, false>>, + { minProperties: 1 } + ), + }), + attributes: Required({ + comment: + "A list of attributes with values above 8. Attributes that are not listed here default to 8.", + type: Array(IncludeIdentifier(Attribute)), + }), + derivedCharacteristics: Required({ + comment: "Computed, set and bought values for/of derived characteristics.", + type: Object_({ + lifePoints: Required({ + type: IncludeIdentifier(Energy), + }), + arcaneEnergy: Optional({ + comment: + "This property does not exist if the character does not have the active advantage Spellcaster and an active magical tradition special ability.", + type: IncludeIdentifier(EnergyWithBuyBack), + }), + karmaPoints: Optional({ + comment: + "This property does not exist if the character does not have the active advantage Blessed One and an active blessed tradition special ability.", + type: IncludeIdentifier(EnergyWithBuyBack), + }), + spirit: Required({ + type: IncludeIdentifier(DerivedCharacteristic), + }), + toughness: Required({ + type: IncludeIdentifier(DerivedCharacteristic), + }), + dodge: Required({ + type: IncludeIdentifier(DerivedCharacteristic), + }), + initiative: Required({ + type: IncludeIdentifier(DerivedCharacteristic), + }), + movement: Required({ + type: IncludeIdentifier(DerivedCharacteristic), + }), + woundThreshold: Optional({ + comment: "This property is not present if the required focus rules are not activated.", + type: IncludeIdentifier(DerivedCharacteristic), + }), + }), + }), + skills: Required({ + comment: + "A list of skills with values above 0. Skills that are not listed here default to 0.", + type: Array(IncludeIdentifier(Skill)), + }), + combatTechniques: Required({ + comment: "Lists of combat techniques, by group.", + type: Object_({ + melee: Required({ + comment: + "A list of melee combat techniques with values above 6. Combat techniques that are not listed here default to 6.", + type: Array(IncludeIdentifier(CombatTechnique)), + }), + ranged: Required({ + comment: + "A list of ranged combat techniques with values above 6. Combat techniques that are not listed here default to 6.", + type: Array(IncludeIdentifier(CombatTechnique)), + }), + }), + }), + cantrips: Optional({ + comment: "A list of purchased cantrips.", + type: Array(IncludeIdentifier(TinyActivatable), { minItems: 1 }), + }), + spells: Optional({ + comment: "A list of active spells.", + type: Array(IncludeIdentifier(ActivatableRatedWithEnhancements), { minItems: 1 }), + }), + rituals: Optional({ + comment: "A list of active rituals.", + type: Array(IncludeIdentifier(ActivatableRatedWithEnhancements), { minItems: 1 }), + }), + magicalActions: Optional({ + comment: + "Lists of magical actions, by group.\n\nThis object may grow if new magical actions are introduced, which is not considered a breaking change.", + type: Object_( + Object.fromEntries( + Object.values(MagicalActionIdentifier.type.value.values).map(caseRef => [ + caseRef.type.entity.namePlural.slice(0, 1).toLowerCase() + + caseRef.type.entity.namePlural.slice(1), + Optional({ type: Array(IncludeIdentifier(ActivatableRated), { minItems: 1 }) }), + ]) + ) as Record< + string, + MemberDecl>, false> + >, + { minProperties: 1 } + ), + }), + blessings: Optional({ + comment: "A list of purchased blessings.", + type: Array(IncludeIdentifier(TinyActivatable), { minItems: 1 }), + }), + liturgicalChants: Optional({ + comment: "A list of active liturgical chants.", + type: Array(IncludeIdentifier(ActivatableRatedWithEnhancements), { minItems: 1 }), + }), + ceremonies: Optional({ + comment: "A list of active ceremonies.", + type: Array(IncludeIdentifier(ActivatableRatedWithEnhancements), { minItems: 1 }), + }), + + // items: {} + + // hit_zone_armors?: {} + + purse: Required({ + comment: "The money the character owns.", + type: IncludeIdentifier(Purse), + }), + + // creatures?: {} + + // pact?: {} + }), +}) diff --git a/src/creation.ts b/src/creation.ts new file mode 100644 index 0000000..620b0d9 --- /dev/null +++ b/src/creation.ts @@ -0,0 +1,103 @@ +import { + Boolean, + IncludeIdentifier, + Object, + Optional, + Required, + String, + TypeAlias, +} from "tsondb/schema/def" +import { READ_ONLY_NOTE, UUIDv4 } from "./utils.js" + +export const ExperienceLevel = TypeAlias(import.meta.filename, { + name: "ExperienceLevel", + comment: + "An object storing the identifiers of the experience level the character started at and the current experience level, derived from total adventure points.", + type: () => + Object({ + start: Required({ + comment: "The start experience level identifier.", + type: IncludeIdentifier(UUIDv4), + }), + current: Required({ + comment: "The current experience level identifier." + READ_ONLY_NOTE, + type: IncludeIdentifier(UUIDv4), + }), + }), +}) + +export const Race = TypeAlias(import.meta.filename, { + name: "Race", + comment: "An object storing the identifiers of the race and its optional race variant.", + type: () => + Object({ + base: Required({ + comment: "The base race.", + type: IncludeIdentifier(UUIDv4), + }), + variant: Optional({ + comment: "The race variant.", + type: IncludeIdentifier(UUIDv4), + }), + selectedAttributeAdjustment: Required({ + comment: "The identifier of the attribute adjustment that has been selected from the race.", + type: IncludeIdentifier(UUIDv4), + }), + }), +}) + +export const Culture = TypeAlias(import.meta.filename, { + name: "Culture", + comment: + "An object storing the identifier of the culture and if the cultural package has been applied.", + type: () => + Object({ + base: Required({ + comment: "The culture identifier.", + type: IncludeIdentifier(UUIDv4), + }), + isCulturalPackageApplied: Required({ + comment: + "Describes whether the cultural package has been applied when creating the character.", + type: Boolean(), + }), + }), +}) + +export const Profession = TypeAlias(import.meta.filename, { + name: "Profession", + comment: + "An object storing the identifiers of the profession, its instance and its optional profession variant.", + type: () => + Object({ + package: Required({ + comment: + "The profession instance identifier. Profession instances are versions of the same profession but slightly different values, such as in extension rule books existing professions might get an additional style special ability.", + type: IncludeIdentifier(UUIDv4), + }), + variant: Optional({ + comment: "The profession variant identifier.", + type: IncludeIdentifier(UUIDv4), + }), + customName: Optional({ + comment: "A custom name for the profession, if provided.", + type: String({ minLength: 1 }), + }), + }), +}) + +export const Curriculum = TypeAlias(import.meta.filename, { + name: "Curriculum", + comment: "An object storing the identifiers of the curriculum and it’s optional lesson package.", + type: () => + Object({ + educationalInstitution: Required({ + comment: "The educational institution's curriculum identifier.", + type: IncludeIdentifier(UUIDv4), + }), + lessonPackage: Optional({ + comment: "The lesson package identifier.", + type: IncludeIdentifier(UUIDv4), + }), + }), +}) diff --git a/src/derivedCharacteristics.ts b/src/derivedCharacteristics.ts new file mode 100644 index 0000000..e828c77 --- /dev/null +++ b/src/derivedCharacteristics.ts @@ -0,0 +1,90 @@ +import { Integer, Object, Required, TypeAlias } from "tsondb/schema/def" +import { READ_ONLY_NOTE } from "./utils.js" + +export const DerivedCharacteristic = TypeAlias(import.meta.filename, { + name: "DerivedCharacteristic", + comment: "A derived characteristic with its calculated parts.", + type: () => + Object({ + base: Required({ + comment: "The base value." + READ_ONLY_NOTE, + type: Integer(), + }), + modifier: Required({ + comment: + "The modifier depending on relevant advantages, disadvantages and special abilities." + + READ_ONLY_NOTE, + type: Integer(), + }), + value: Required({ + comment: "The (final) value." + READ_ONLY_NOTE, + type: Integer(), + }), + }), +}) + +export const Energy = TypeAlias(import.meta.filename, { + name: "Energy", + comment: + "A derived characteristic representing an energy where points can be purchased and permanently lost.", + type: () => + Object({ + base: Required({ + comment: "The base value." + READ_ONLY_NOTE, + type: Integer(), + }), + modifier: Required({ + comment: + "The modifier depending on relevant advantages, disadvantages and special abilities." + + READ_ONLY_NOTE, + type: Integer(), + }), + purchased: Required({ + comment: "The number of points purchased.", + type: Integer({ minimum: 0 }), + }), + permanently_lost: Required({ + comment: "The number of points permanently lost.", + type: Integer({ minimum: 0 }), + }), + maximum: Required({ + comment: "The (final) maximum." + READ_ONLY_NOTE, + type: Integer(), + }), + }), +}) + +export const EnergyWithBuyBack = TypeAlias(import.meta.filename, { + name: "EnergyWithBuyBack", + comment: + "A derived characteristic representing an energy where points can be purchased and permanently lost, with the option to buy back lost points.", + type: () => + Object({ + base: Required({ + comment: "The base value." + READ_ONLY_NOTE, + type: Integer(), + }), + modifier: Required({ + comment: + "The modifier depending on relevant advantages, disadvantages and special abilities." + + READ_ONLY_NOTE, + type: Integer(), + }), + purchased: Required({ + comment: "The number of points purchased.", + type: Integer({ minimum: 0 }), + }), + permanently_lost: Required({ + comment: "The number of points permanently lost.", + type: Integer({ minimum: 0 }), + }), + permanently_lost_bought_back: Required({ + comment: "The number of permanently lost points that have been bought back.", + type: Integer({ minimum: 0 }), + }), + maximum: Required({ + comment: "The (final) maximum." + READ_ONLY_NOTE, + type: Integer(), + }), + }), +}) diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..2a3fbe2 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,16 @@ +import * as TSONDB from "tsondb/schema" +import { Character } from "./character.js" + +export const Schema = TSONDB.Schema([Character]) + +export * from "./activatables.js" +export * from "./adventurePoints.js" +export * from "./belongings.js" +export * from "./character.js" +export * from "./creation.js" +export * from "./derivedCharacteristics.js" +export * from "./personalData.js" +export * from "./rated.js" +export * from "./rules.js" +export * from "./sex.js" +export { UUIDv4 } from "./utils.js" diff --git a/src/personalData.ts b/src/personalData.ts new file mode 100644 index 0000000..0124561 --- /dev/null +++ b/src/personalData.ts @@ -0,0 +1,87 @@ +import { + Enum, + EnumCase, + IncludeIdentifier, + Object, + Optional, + Required, + String, + TypeAlias, +} from "tsondb/schema/def" +import { UUIDv4 } from "./utils.js" + +export const PersonalData = TypeAlias(import.meta.filename, { + name: "PersonalData", + comment: "Personal data such as hair color and place of birth.", + type: () => + Object({ + sex: Required({ + comment: + "The character’s sex. It does not have to be binary, although it always must be specified how to handle it in the context of binary sex prerequisites. You can also provide a custom sex with a custom name.", + type: String({ minLength: 1 }), + }), + family: Optional({ + comment: "The family names and/or family members.", + type: String({ minLength: 1 }), + }), + placeOfBirth: Optional({ + comment: "The place where the character was born.", + type: String({ minLength: 1 }), + }), + dateOfBirth: Optional({ + comment: "The date when the character was born.", + type: String({ minLength: 1 }), + }), + age: Optional({ + comment: "The age of the character.", + type: String({ minLength: 1 }), + }), + hairColor: Optional({ + comment: "The hair color of the character.", + type: IncludeIdentifier(Color), + }), + eyeColor: Optional({ + comment: "The eye color of the character.", + type: IncludeIdentifier(Color), + }), + size: Optional({ + comment: "The size of the character.", + type: String({ minLength: 1 }), + }), + weight: Optional({ + comment: "The weight of the character.", + type: String({ minLength: 1 }), + }), + title: Optional({ + comment: "The character’s title(s).", + type: String({ minLength: 1 }), + }), + socialStatus: Optional({ + comment: "The social status.", + type: IncludeIdentifier(UUIDv4), + }), + characteristics: Optional({ + comment: "The character’s characteristics.", + type: String({ minLength: 1 }), + }), + otherInfo: Optional({ + comment: "Other information about the character.", + type: String({ minLength: 1 }), + }), + }), +}) + +const Color = Enum(import.meta.filename, { + name: "Color", + comment: "A color, either predefined or custom.", + values: () => ({ + Predefined: EnumCase({ + comment: "A predefined color.", + type: IncludeIdentifier(UUIDv4), + }), + Custom: EnumCase({ + comment: "A custom color.", + type: String({ minLength: 1 }), + }), + }), +}) diff --git a/src/rated.ts b/src/rated.ts new file mode 100644 index 0000000..e5d3a11 --- /dev/null +++ b/src/rated.ts @@ -0,0 +1,89 @@ +import { Array, IncludeIdentifier, Integer, Object, Required, TypeAlias } from "tsondb/schema/def" +import { UUIDv4 } from "./utils.js" + +export const Attribute = TypeAlias(import.meta.filename, { + name: "Attribute", + comment: "An attribute with its value.", + type: () => + Object({ + id: Required({ + comment: "The attribute identifier.", + type: IncludeIdentifier(UUIDv4), + }), + value: Required({ + comment: "The attribute value.", + type: Integer({ minimum: 9 }), + }), + }), +}) + +export const Skill = TypeAlias(import.meta.filename, { + name: "Skill", + comment: "A skill with its rating.", + type: () => + Object({ + id: Required({ + comment: "The skill identifier.", + type: IncludeIdentifier(UUIDv4), + }), + value: Required({ + comment: "The skill rating.", + type: Integer({ minimum: 1 }), + }), + }), +}) + +export const CombatTechnique = TypeAlias(import.meta.filename, { + name: "CombatTechnique", + comment: "A combat technique with its rating.", + type: () => + Object({ + id: Required({ + comment: "The combat technique identifier.", + type: IncludeIdentifier(UUIDv4), + }), + value: Required({ + comment: "The combat technique rating.", + type: Integer({ minimum: 7 }), + }), + }), +}) + +export const ActivatableRated = TypeAlias(import.meta.filename, { + name: "ActivatableRated", + comment: "An activatable item with its rating.", + type: () => + Object({ + id: Required({ + comment: "The skill identifier.", + type: IncludeIdentifier(UUIDv4), + }), + value: Required({ + comment: "The skill rating.", + type: Integer({ minimum: 0 }), + }), + }), +}) + +export const ActivatableRatedWithEnhancements = TypeAlias(import.meta.filename, { + name: "ActivatableRatedWithEnhancements", + comment: "An activatable item with its rating and purchased enhancements.", + type: () => + Object({ + id: Required({ + comment: "The skill identifier.", + type: IncludeIdentifier(UUIDv4), + }), + value: Required({ + comment: "The skill rating.", + type: Integer({ minimum: 0 }), + }), + enhancements: Required({ + comment: "Purchased enhancements.", + type: Array(IncludeIdentifier(UUIDv4), { + minItems: 1, + uniqueItems: true, + }), + }), + }), +}) diff --git a/src/rules.ts b/src/rules.ts new file mode 100644 index 0000000..4473e61 --- /dev/null +++ b/src/rules.ts @@ -0,0 +1,65 @@ +import { + Array, + Boolean, + IncludeIdentifier, + Object, + Optional, + Required, + TypeAlias, +} from "tsondb/schema/def" +import { UUIDv4 } from "./utils.js" + +export const Rules = TypeAlias(import.meta.filename, { + name: "Rules", + comment: "The rules settings for the character.", + type: () => + Object({ + includeAllPublications: Required({ + comment: + "Whether the character makes use of all publications except for publications with adult content and adventures, which have to be specified explicitly using `includePublications`.", + type: Boolean(), + }), + includePublications: Required({ + comment: + "Explicitly used publications. If `includeAllPublications` is set to `true`, only affects publications that are not covered by `includeAllPublications`.", + type: Array(IncludeIdentifier(UUIDv4)), + }), + focusRules: Required({ + comment: "Active focus rules.", + type: Array(IncludeIdentifier(FocusRule)), + }), + optionalRules: Required({ + comment: "Active optional rules. Each optional rule may have one or more options selected.", + type: Array(IncludeIdentifier(OptionalRule)), + }), + }), +}) + +export const FocusRule = TypeAlias(import.meta.filename, { + name: "FocusRule", + comment: "An active focus rule.", + type: () => + Object({ + id: Required({ + comment: "The focus rule identifier.", + type: IncludeIdentifier(UUIDv4), + }), + }), +}) + +export const OptionalRule = TypeAlias(import.meta.filename, { + name: "OptionalRule", + comment: "An active optional rule. An optional rule may have one or more options selected.", + type: () => + Object({ + id: Required({ + comment: "The optional rule identifier.", + type: IncludeIdentifier(UUIDv4), + }), + options: Optional({ + comment: + "An array of one or more options. The exact meaning of each option varies based on the optional rule.", + type: Array(IncludeIdentifier(UUIDv4), { minItems: 1 }), + }), + }), +}) diff --git a/src/sex.ts b/src/sex.ts new file mode 100644 index 0000000..a77f3ef --- /dev/null +++ b/src/sex.ts @@ -0,0 +1,67 @@ +import { + Boolean, + Enum, + EnumCase, + IncludeIdentifier, + Object, + Required, + String, + TypeAlias, +} from "tsondb/schema/def" + +export const Sex = Enum(import.meta.filename, { + name: "Sex", + comment: + "The character’s sex. It does not have to be binary, although it always must be specified how to handle it in the context of binary sex prerequisites. You can also provide a custom sex with a custom name.", + values: () => ({ + Male: EnumCase({ type: null }), + Female: EnumCase({ type: null }), + BalThani: EnumCase({ type: IncludeIdentifier(NonBinarySex) }), + Tsajana: EnumCase({ type: IncludeIdentifier(NonBinarySex) }), + Custom: EnumCase({ type: IncludeIdentifier(CustomSex) }), + }), +}) + +export const NonBinarySex = TypeAlias(import.meta.filename, { + name: "NonBinarySex", + comment: "A non-binary sex option.", + type: () => + Object({ + binaryHandling: Required({ + comment: "Defines how a sex should be treated when checking prerequisites.", + type: IncludeIdentifier(BinaryHandling), + }), + }), +}) + +export const CustomSex = TypeAlias(import.meta.filename, { + name: "CustomSex", + comment: "A custom non-binary sex option.", + type: () => + Object({ + name: Required({ + comment: "The custom sex name.", + type: String({ minLength: 1 }), + }), + binaryHandling: Required({ + comment: "Defines how a sex should be treated when checking prerequisites.", + type: IncludeIdentifier(BinaryHandling), + }), + }), +}) + +export const BinaryHandling = TypeAlias(import.meta.filename, { + name: "BinaryHandling", + comment: "Defines how a sex should be treated when checking prerequisites.", + type: () => + Object({ + asMale: Required({ + comment: "Defines if the sex should be treated as male when checking prerequisites.", + type: Boolean(), + }), + asFemale: Required({ + comment: "Defines if the sex should be treated as female when checking prerequisites.", + type: Boolean(), + }), + }), +}) diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..19f7964 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,13 @@ +import { String, TypeAlias } from "tsondb/schema/def" + +export const READ_ONLY_NOTE = + "\n\n*Note: This value is read-only and recalculated by Optolith. Setting it to a different value will not influence calculation in Optolith.*" + +export const UUIDv4 = TypeAlias(import.meta.filename, { + name: "UUIDv4", + comment: "An UUIDv4 identifier of an entry in the Optolith database.", + type: () => + String({ + pattern: /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/, + }), +}) diff --git a/tsondb.config.ts b/tsondb.config.ts new file mode 100644 index 0000000..188813d --- /dev/null +++ b/tsondb.config.ts @@ -0,0 +1,19 @@ +import { join } from "node:path" +import type { GenerationConfig } from "tsondb/config" +import { JsonSchemaOutput } from "tsondb/renderer/jsonschema" +import { TypeScriptOutput } from "tsondb/renderer/ts" +import { Schema } from "./lib/index.js" + +const config: GenerationConfig = { + schema: Schema, + outputs: [ + TypeScriptOutput({ + targetPath: join(import.meta.dirname, "gen", "types.d.ts"), + }), + JsonSchemaOutput({ + targetPath: join(import.meta.dirname, "gen", "types.schema.json"), + }), + ], +} + +export default config