Compare commits

..

No commits in common. "main" and "2.3.2" have entirely different histories.
main ... 2.3.2

118 changed files with 871 additions and 1181 deletions

View file

@ -1,6 +1,5 @@
[*]
indent_size = 4
indent_style = spaces
end_of_line = lf
[*.yml]
indent_size = 2

View file

@ -1,2 +1,2 @@
FOUNDRY_MAIN_PATH=/path/to/foundry/resources/app/main.js
FOUNDRY_DATA_PATH=/path/to/foundry/
FOUNDRY_DATA_PATH=/path/to/foundry/data

13
.prettierrc Normal file
View file

@ -0,0 +1,13 @@
{
"trailingComma": "none",
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": true,
"quoteProps": "consistent",
"bracketSpacing": true,
"arrowParens": "avoid",
"printWidth": 120,
"endOfLine": "lf",
"bracketSameLine": true
}

View file

@ -38,13 +38,11 @@ You can find the documentation here: https://github.com/Foundryborne/daggerheart
3. **Configure your Foundry paths:**
Copy `.env.example` to `.env` and set up the foundry path and data path. The foundry path must point to main.js, and the data path must point the folder containing the Data folder itself (but not the `Data` folder itself).
```bash
npm run setup:dev -- --foundry-path="/path/to/foundry/main.js" --data-path="/path/to/data"
```
4. **Setup symlinks:**
In a shell with elevated privilages, run `npm run createSymlink`. It will add a foundry folder for types, and add a symlink to the daggerheart folder into your foundry data.
5. **Start developing:**
4. **Start developing:**
```bash
npm start
```
@ -53,7 +51,7 @@ You can find the documentation here: https://github.com/Foundryborne/daggerheart
- `npm start` - Start development with file watching and Foundry launching
- `npm run build` - One-time build
- `npm run createSymlink` - Setup types and output symlinks
- `npm run setup:dev -- --foundry-path="<path>" --data-path="<path>"` - Configure development environment
### Notes

24
daggerheart.d.ts vendored
View file

@ -1,11 +1,8 @@
import '@client/global.mjs';
import '@common/global.mjs';
import '@common/primitives/global.mjs';
import Canvas from '@client/canvas/board.mjs';
// Foundry's use of `Object.assign(globalThis) means many globally available objects are not read as such
// This declare global hopefully fixes that
// Note: eslint is not aware of these, whatever is added here should go in the eslint's globals list
declare global {
/**
* A simple event framework used throughout Foundry Virtual Tabletop.
@ -15,28 +12,9 @@ declare global {
class Hooks extends foundry.helpers.Hooks {}
const fromUuid = foundry.utils.fromUuid;
const fromUuidSync = foundry.utils.fromUuidSync;
/**
* A representation of a color in hexadecimal format.
* This class provides methods for transformations and manipulations of colors.
*/
class Color extends foundry.utils.Color {}
/**
* The singleton game canvas
*/
const canvas: Canvas;
const ActiveEffect: foundry.documents.ActiveEffect;
const Actor: foundry.documents.Actor;
const BaseScene: foundry.documents.BaseScene;
const ChatMessage: foundry.documents.ChatMessage;
const Combat: foundry.documents.Combat;
const Combatant: foundry.documents.Combatant;
const Item: foundry.documents.Item;
const Macro: foundry.documents.Macro;
const Scene: foundry.documents.Scene;
const TokenDocument: foundry.documents.TokenDocument;
const Collection: foundry.utils.Collection;
const FormDataExtended: foundry.applications.ux.FormDataExtended;
const TextEditor: foundry.applications.ux.TextEditor;
}

View file

@ -1,101 +1,14 @@
import globals from 'globals';
import { defineConfig, globalIgnores } from 'eslint/config';
import tseslint from 'typescript-eslint';
import js from '@eslint/js';
import stylistic from '@stylistic/eslint-plugin';
/** @type {Partial<RulesConfig>} */
export const stylisticRules = {
'@stylistic/indent': [
'error',
4,
{
SwitchCase: 1
}
],
'@stylistic/max-len': ['error', {
code: 120,
ignoreComments: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
ignoreRegExpLiterals: true
}],
'@stylistic/quotes': ['error', 'single', { allowTemplateLiterals: 'always' }],
'@stylistic/arrow-parens': ['error', 'as-needed'],
'@stylistic/quote-props': ['error', 'as-needed'],
'@stylistic/array-bracket-newline': ['error', 'consistent'],
'@stylistic/key-spacing': 'error',
'@stylistic/comma-dangle': ['error', 'never'],
'@stylistic/space-in-parens': ['error', 'never'],
'@stylistic/space-infix-ops': 2,
'@stylistic/keyword-spacing': 2,
'@stylistic/semi-spacing': 2,
'@stylistic/no-multi-spaces': 2,
'@stylistic/no-extra-semi': 2,
'@stylistic/no-whitespace-before-property': 2,
'@stylistic/space-unary-ops': 2
};
import { defineConfig } from 'eslint/config';
import prettier from 'eslint-plugin-prettier';
export default defineConfig([
globalIgnores(['foundry/**/*', 'build/**/*']),
{
files: ['gulpfile.js', 'postcss.config.js'],
languageOptions: { globals: globals.node }
},
{ files: ['**/*.{js,mjs,cjs}'], languageOptions: { globals: globals.browser } },
{ plugins: { prettier } },
{
files: ['**/*.{js,mjs,cjs}'],
plugins: {
'@stylistic': stylistic
},
languageOptions: {
globals: {
...globals.browser,
CONFIG: 'readonly',
CONST: 'readonly',
// Global classes
Color: 'readonly',
Handlebars: 'readonly',
Hooks: 'readonly',
PIXI: 'readonly',
ProseMirror: 'readonly',
Roll: 'readonly',
// global namespaces
canvas: 'readonly',
foundry: 'readonly',
game: 'readonly',
ui: 'readonly',
// global functions
fromUuid: 'readonly',
fromUuidSync: 'readonly',
getDocumentClass: 'readonly',
_del: 'readonly',
_replace: 'readonly',
_loc: 'readonly',
// Documents
ActiveEffect: 'readonly',
Actor: 'readonly',
BaseScene: 'readonly',
ChatMessage: 'readonly',
Combat: 'readonly',
Combatant: 'readonly',
Item: 'readonly',
Macro: 'readonly',
Scene: 'readonly',
TokenDocument: 'readonly',
// Other
Collection: 'readonly',
FormDataExtended: 'readonly',
TextEditor: 'readonly'
}
},
rules: {
'no-undef': 'error',
// 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
...stylisticRules
'prettier/prettier': 'error'
}
},
{
files: ['**/*.ts'],
extends: [js.configs.recommended, tseslint.configs.recommended]
}
]);

View file

@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "es2022",
"target": "es2022",
"module": "ES6",
"target": "ES6",
"paths": {
"@client/*": ["./foundry/client/*"],
"@common/*": ["./foundry/common/*"]

View file

@ -450,7 +450,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
if (equipment.includes(type))
presets.filter = {
'system.tier': { key: 'system.tier', value: 1 },
type: { key: 'type', value: type }
'type': { key: 'type', value: type }
};
ui.compendiumBrowser.open(presets);

View file

@ -138,12 +138,12 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
const stressReductionStress = this.availableStressReductions
? stressReductions.reduce((acc, red) => acc + red.cost, 0)
: 0;
const stress = this.actor.system.resources.stress;
context.stress =
selectedStressMarks.length > 0 || this.availableStressReductions
? {
value: stress.value + selectedStressMarks.length + stressReductionStress,
max: stress.max
value:
this.actor.system.resources.stress.value + selectedStressMarks.length + stressReductionStress,
max: this.actor.system.resources.stress.max
}
: null;

View file

@ -135,6 +135,192 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
context.tabs.advancements.progress = { selected: selections, max: currentLevel.maxSelections };
context.showTabs = this.tabGroups.primary !== 'summary';
break;
const actorArmor = this.actor.system.armor;
const levelKeys = Object.keys(this.levelup.levels);
let achivementProficiency = 0;
const achievementCards = [];
let achievementExperiences = [];
for (var levelKey of levelKeys) {
const level = this.levelup.levels[levelKey];
if (Number(levelKey) < this.levelup.startLevel) continue;
achivementProficiency += level.achievements.proficiency ?? 0;
const cards = level.achievements.domainCards ? Object.values(level.achievements.domainCards) : null;
if (cards) {
for (var card of cards) {
const itemCard = await foundry.utils.fromUuid(card.uuid);
achievementCards.push(itemCard);
}
}
achievementExperiences = level.achievements.experiences
? Object.values(level.achievements.experiences).reduce((acc, experience) => {
if (experience.name) acc.push(experience);
return acc;
}, [])
: [];
}
context.achievements = {
proficiency: {
old: this.actor.system.proficiency,
new: this.actor.system.proficiency + achivementProficiency,
shown: achivementProficiency > 0
},
damageThresholds: {
major: {
old: this.actor.system.damageThresholds.major,
new: this.actor.system.damageThresholds.major + changedActorLevel - currentActorLevel
},
severe: {
old: this.actor.system.damageThresholds.severe,
new:
this.actor.system.damageThresholds.severe +
(actorArmor
? changedActorLevel - currentActorLevel
: (changedActorLevel - currentActorLevel) * 2)
},
unarmored: !actorArmor
},
domainCards: {
values: achievementCards,
shown: achievementCards.length > 0
},
experiences: {
values: achievementExperiences
}
};
const advancement = {};
for (var levelKey of levelKeys) {
const level = this.levelup.levels[levelKey];
if (Number(levelKey) < this.levelup.startLevel) continue;
for (var choiceKey of Object.keys(level.choices)) {
const choice = level.choices[choiceKey];
for (var checkbox of Object.values(choice)) {
switch (choiceKey) {
case 'proficiency':
case 'hitPoint':
case 'stress':
case 'evasion':
advancement[choiceKey] = advancement[choiceKey]
? advancement[choiceKey] + Number(checkbox.value)
: Number(checkbox.value);
break;
case 'trait':
if (!advancement[choiceKey]) advancement[choiceKey] = {};
for (var traitKey of checkbox.data) {
if (!advancement[choiceKey][traitKey]) advancement[choiceKey][traitKey] = 0;
advancement[choiceKey][traitKey] += 1;
}
break;
case 'domainCard':
if (!advancement[choiceKey]) advancement[choiceKey] = [];
if (checkbox.data.length === 1) {
const choiceItem = await foundry.utils.fromUuid(checkbox.data[0]);
advancement[choiceKey].push(choiceItem.toObject());
}
break;
case 'experience':
if (!advancement[choiceKey]) advancement[choiceKey] = [];
const data = checkbox.data.map(data => {
const experience = Object.keys(this.actor.system.experiences).find(
x => x === data
);
return this.actor.system.experiences[experience]?.description ?? '';
});
advancement[choiceKey].push({ data: data, value: checkbox.value });
break;
case 'subclass':
if (checkbox.data[0]) {
const subclassItem = await foundry.utils.fromUuid(checkbox.data[0]);
if (!advancement[choiceKey]) advancement[choiceKey] = [];
advancement[choiceKey].push({
...subclassItem.toObject(),
featureLabel: game.i18n.localize(
subclassFeatureLabels[Number(checkbox.secondaryData.featureState)]
)
});
}
break;
case 'multiclass':
const multiclassItem = await foundry.utils.fromUuid(checkbox.data[0]);
const subclass = multiclassItem
? await foundry.utils.fromUuid(checkbox.secondaryData.subclass)
: null;
advancement[choiceKey] = multiclassItem
? {
...multiclassItem.toObject(),
domain: checkbox.secondaryData.domain
? game.i18n.localize(
CONFIG.DH.DOMAIN.allDomains()[checkbox.secondaryData.domain]
.label
)
: null,
subclass: subclass ? subclass.name : null
}
: {};
break;
}
}
}
}
context.advancements = {
statistics: {
proficiency: {
old: context.achievements.proficiency.new,
new: context.achievements.proficiency.new + (advancement.proficiency ?? 0)
},
hitPoints: {
old: this.actor.system.resources.hitPoints.max,
new: this.actor.system.resources.hitPoints.max + (advancement.hitPoint ?? 0)
},
stress: {
old: this.actor.system.resources.stress.max,
new: this.actor.system.resources.stress.max + (advancement.stress ?? 0)
},
evasion: {
old: this.actor.system.evasion,
new: this.actor.system.evasion + (advancement.evasion ?? 0)
}
},
traits: Object.keys(this.actor.system.traits).reduce((acc, traitKey) => {
if (advancement.trait?.[traitKey]) {
if (!acc) acc = {};
acc[traitKey] = {
label: game.i18n.localize(abilities[traitKey].label),
old: this.actor.system.traits[traitKey].value,
new: this.actor.system.traits[traitKey].value + advancement.trait[traitKey]
};
}
return acc;
}, null),
domainCards: advancement.domainCard ?? [],
experiences:
advancement.experience?.flatMap(x => x.data.map(data => ({ name: data, modifier: x.value }))) ??
[],
multiclass: advancement.multiclass,
subclass: advancement.subclass
};
context.advancements.statistics.proficiency.shown =
context.advancements.statistics.proficiency.new > context.advancements.statistics.proficiency.old;
context.advancements.statistics.hitPoints.shown =
context.advancements.statistics.hitPoints.new > context.advancements.statistics.hitPoints.old;
context.advancements.statistics.stress.shown =
context.advancements.statistics.stress.new > context.advancements.statistics.stress.old;
context.advancements.statistics.evasion.shown =
context.advancements.statistics.evasion.new > context.advancements.statistics.evasion.old;
context.advancements.statistics.shown =
context.advancements.statistics.proficiency.shown ||
context.advancements.statistics.hitPoints.shown ||
context.advancements.statistics.stress.shown ||
context.advancements.statistics.evasion.shown;
break;
}
return context;
@ -198,7 +384,9 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
this._dragDrop.forEach(d => d.bind(htmlElement));
}
tagifyUpdate = type => async (_, { option, removed }) => {
tagifyUpdate =
type =>
async (_, { option, removed }) => {
const updatePath = Object.keys(this.levelup.levels[this.levelup.currentLevel].choices).reduce(
(acc, choiceKey) => {
const choice = this.levelup.levels[this.levelup.currentLevel].choices[choiceKey];

View file

@ -306,7 +306,7 @@ export default class Party extends DHBaseActorSheet {
static async downtimeMoveQuery({ actorId, downtimeType }) {
const actor = await foundry.utils.fromUuid(actorId);
if (!actor || !actor?.isOwner) return;
if (!actor || !actor?.isOwner) reject();
new game.system.api.applications.dialogs.Downtime(actor, downtimeType === 'shortRest').render({
force: true
});

View file

@ -377,7 +377,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
action: 'update',
documentName: 'Item',
parent: targetActor,
updates: [{ _id: existing.id, 'system.quantity': existing.system.quantity + quantity }]
updates: [{ '_id': existing.id, 'system.quantity': existing.system.quantity + quantity }]
});
} else {
const itemsToCreate = [];
@ -410,7 +410,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
action: 'update',
documentName: 'Item',
parent: originActor,
updates: [{ _id: item.id, 'system.quantity': item.system.quantity - quantity }]
updates: [{ '_id': item.id, 'system.quantity': item.system.quantity - quantity }]
});
}

View file

@ -29,6 +29,16 @@ export default function ItemAttachmentSheet(Base) {
}
};
async _preparePartContext(partId, context) {
await super._preparePartContext(partId, context);
if (partId === 'attachments') {
context.attachedItems = await prepareAttachmentContext(this.document);
}
return context;
}
async _onDrop(event) {
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);

View file

@ -50,7 +50,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
const title =
(flavor ?? traitValue)
? game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
ability: game.i18n.localize(CONFIG.DH.ACTOR.abilities[traitValue].label)
ability: game.i18n.localize(SYSTEM.ACTOR.abilities[traitValue].label)
})
: game.i18n.localize('DAGGERHEART.GENERAL.duality');

View file

@ -148,7 +148,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio
}
async gmSetSetting(data) {
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data);
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data),
game.socket.emit(`system.${CONFIG.DH.id}`, {
action: socketEvent.Refresh,
data: { refreshType: RefreshType.Countdown }

View file

@ -212,7 +212,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
}
static async gmSetSetting(data) {
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data);
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data),
game.socket.emit(`system.${CONFIG.DH.id}`, {
action: socketEvent.Refresh,
data: { refreshType: RefreshType.Countdown }

View file

@ -223,7 +223,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
* @returns {object}
*/
async use(event, configOptions = {}) {
if (!this.actor) throw new Error('An Action can\'t be used outside of an Actor context.');
if (!this.actor) throw new Error("An Action can't be used outside of an Actor context.");
let config = this.prepareConfig(event, configOptions);
if (!config) return;

View file

@ -90,13 +90,13 @@ export default class BeastformEffect extends BaseEffect {
...baseUpdate,
x,
y,
texture: {
'texture': {
enabled: this.characterTokenData.usesDynamicToken,
src: token.flags.daggerheart?.beastformTokenImg ?? this.characterTokenData.tokenImg,
scaleX: this.characterTokenData.tokenSize.scale,
scaleY: this.characterTokenData.tokenSize.scale
},
ring: {
'ring': {
subject: {
texture:
token.flags.daggerheart?.beastformSubjectTexture ?? this.characterTokenData.tokenRingImg

View file

@ -520,12 +520,12 @@ export default class DhCharacter extends DhCreature {
if (armorSource.type === 'armor') {
armorUpdates[armorSource.parent.id].updates.push({
_id: armorSource.id,
'_id': armorSource.id,
'system.armor.current': armorSource.system.armor.current + usedArmorChange
});
} else {
effectUpdates[armorSource.parent.id].updates.push({
_id: armorSource.id,
'_id': armorSource.id,
'system.changes': armorSource.system.changes.map(change => ({
...change,
value:

View file

@ -87,7 +87,7 @@ export default class CountdownField extends fields.ArrayField {
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.Countdowns,
countdownSetting.toObject()
);
),
game.socket.emit(`system.${CONFIG.DH.id}`, {
action: socketEvent.Refresh,
data: { refreshType: RefreshType.Countdown }

View file

@ -324,7 +324,7 @@ export default class DHToken extends CONFIG.Token.documentClass {
}
let x = 0.5 * bottom;
let y = 0.25;
for (let k = width - bottom; k--;) {
for (let k = width - bottom; k--; ) {
points.push(x, y);
x += 0.5;
y -= 0.25;
@ -333,7 +333,7 @@ export default class DHToken extends CONFIG.Token.documentClass {
y += 0.25;
}
points.push(x, y);
for (let k = bottom; k--;) {
for (let k = bottom; k--; ) {
y += 0.5;
points.push(x, y);
x += 0.5;
@ -341,14 +341,14 @@ export default class DHToken extends CONFIG.Token.documentClass {
points.push(x, y);
}
y += 0.5;
for (let k = top; k--;) {
for (let k = top; k--; ) {
points.push(x, y);
x -= 0.5;
y += 0.25;
points.push(x, y);
y += 0.5;
}
for (let k = width - top; k--;) {
for (let k = width - top; k--; ) {
points.push(x, y);
x -= 0.5;
y += 0.25;
@ -357,7 +357,7 @@ export default class DHToken extends CONFIG.Token.documentClass {
y -= 0.25;
}
points.push(x, y);
for (let k = top; k--;) {
for (let k = top; k--; ) {
y -= 0.5;
points.push(x, y);
x -= 0.5;
@ -365,7 +365,7 @@ export default class DHToken extends CONFIG.Token.documentClass {
points.push(x, y);
}
y -= 0.5;
for (let k = bottom; k--;) {
for (let k = bottom; k--; ) {
points.push(x, y);
x += 0.5;
y -= 0.25;

551
package-lock.json generated
View file

@ -13,20 +13,18 @@
"rollup": "^4.40.0"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@foundryvtt/foundryvtt-cli": "^1.0.2",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@stylistic/eslint-plugin": "^5.10.0",
"concurrently": "^8.2.2",
"eslint": "^10.2.1",
"eslint-plugin-prettier": "^5.5.5",
"globals": "^17.5.0",
"husky": "^9.1.7",
"lint-staged": "^16.4.0",
"postcss": "^8.4.32",
"rollup-plugin-postcss": "^4.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.60.1"
"prettier": "^3.5.3",
"rollup-plugin-postcss": "^4.0.2"
}
},
"node_modules/@babel/runtime": {
@ -160,27 +158,6 @@
"node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/js": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
"integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://eslint.org/donate"
},
"peerDependencies": {
"eslint": "^10.0.0"
},
"peerDependenciesMeta": {
"eslint": {
"optional": true
}
}
},
"node_modules/@eslint/object-schema": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
@ -443,6 +420,19 @@
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"dev": true
},
"node_modules/@pkgr/core": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
"integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/pkgr"
}
},
"node_modules/@rollup/plugin-commonjs": {
"version": "25.0.8",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz",
@ -771,58 +761,6 @@
"util": "^0.12.4"
}
},
"node_modules/@stylistic/eslint-plugin": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz",
"integrity": "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/types": "^8.56.0",
"eslint-visitor-keys": "^4.2.1",
"espree": "^10.4.0",
"estraverse": "^5.3.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
"eslint": "^9.0.0 || ^10.0.0"
}
},
"node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@stylistic/eslint-plugin/node_modules/espree": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.15.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@trysound/sax": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
@ -857,288 +795,6 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz",
"integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.60.1",
"@typescript-eslint/type-utils": "8.60.1",
"@typescript-eslint/utils": "8.60.1",
"@typescript-eslint/visitor-keys": "8.60.1",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.60.1",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz",
"integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.60.1",
"@typescript-eslint/types": "8.60.1",
"@typescript-eslint/typescript-estree": "8.60.1",
"@typescript-eslint/visitor-keys": "8.60.1",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz",
"integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.60.1",
"@typescript-eslint/types": "^8.60.1",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz",
"integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.60.1",
"@typescript-eslint/visitor-keys": "8.60.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz",
"integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz",
"integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.60.1",
"@typescript-eslint/typescript-estree": "8.60.1",
"@typescript-eslint/utils": "8.60.1",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz",
"integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz",
"integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.60.1",
"@typescript-eslint/tsconfig-utils": "8.60.1",
"@typescript-eslint/types": "8.60.1",
"@typescript-eslint/visitor-keys": "8.60.1",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.5"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
"integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz",
"integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.60.1",
"@typescript-eslint/types": "8.60.1",
"@typescript-eslint/typescript-estree": "8.60.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz",
"integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.60.1",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@yaireo/tagify": {
"version": "4.35.1",
"resolved": "https://registry.npmjs.org/@yaireo/tagify/-/tagify-4.35.1.tgz",
@ -2197,11 +1853,10 @@
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
@ -2586,6 +2241,37 @@
}
}
},
"node_modules/eslint-plugin-prettier": {
"version": "5.5.5",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz",
"integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"prettier-linter-helpers": "^1.0.1",
"synckit": "^0.11.12"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint-plugin-prettier"
},
"peerDependencies": {
"@types/eslint": ">=8.0.0",
"eslint": ">=8.0.0",
"eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
"prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
"@types/eslint": {
"optional": true
},
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-scope": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
@ -2825,6 +2511,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/fast-fifo": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
@ -2861,24 +2554,6 @@
"reusify": "^1.0.4"
}
},
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@ -5542,6 +5217,34 @@
"node": ">= 0.8.0"
}
},
"node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-linter-helpers": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz",
"integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-diff": "^1.1.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -6350,6 +6053,22 @@
"node": ">= 10"
}
},
"node_modules/synckit": {
"version": "0.11.12",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz",
"integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@pkgr/core": "^0.2.9"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/synckit"
}
},
"node_modules/teex": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
@ -6397,23 +6116,6 @@
"node": ">=18"
}
},
"node_modules/tinyglobby": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -6445,19 +6147,6 @@
"tree-kill": "cli.js"
}
},
"node_modules/ts-api-utils": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
"integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18.12"
},
"peerDependencies": {
"typescript": ">=4.8.4"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@ -6482,44 +6171,6 @@
"node": ">= 0.8.0"
}
},
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/typescript-eslint": {
"version": "8.60.1",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz",
"integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.60.1",
"@typescript-eslint/parser": "8.60.1",
"@typescript-eslint/typescript-estree": "8.60.1",
"@typescript-eslint/utils": "8.60.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",

View file

@ -18,25 +18,24 @@
"pullYMLtoLDB": "node ./tools/pullYMLtoLDB.mjs",
"pullYMLtoLDBBuild": "node ./tools/pullYMLtoLDB.mjs --build",
"createSymlink": "node ./tools/create-symlink.mjs",
"setup:dev": "node ./tools/dev-setup.mjs",
"lint": "eslint",
"lint:fix": "eslint --fix",
"prepare": "husky"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@foundryvtt/foundryvtt-cli": "^1.0.2",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@stylistic/eslint-plugin": "^5.10.0",
"concurrently": "^8.2.2",
"eslint": "^10.2.1",
"eslint-plugin-prettier": "^5.5.5",
"globals": "^17.5.0",
"husky": "^9.1.7",
"lint-staged": "^16.4.0",
"postcss": "^8.4.32",
"rollup-plugin-postcss": "^4.0.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.60.1"
"prettier": "^3.5.3",
"rollup-plugin-postcss": "^4.0.2"
},
"lint-staged": {
"**/*": "eslint --fix"

View file

@ -1,11 +1,19 @@
@import './less/sheets/index.less';
@import './less/sheets-settings/index.less';
@import './less/dialog/index.less';
@import './less/hud/index.less';
@import './less/utils/index.less';
@import './less//hud/index.less';
@import './less/utils/colors.less';
@import './less/utils/fonts.less';
@import './less/global/index.less';
@import './less/ui/index.less';
@import './less/ux/index.less';
@import '../build/tagify.css';
@import './less/utils/mixin.less';

View file

@ -1 +0,0 @@
@import "./action-list.less";

View file

@ -1 +0,0 @@
@import "./sheet.less";

View file

@ -1 +0,0 @@
@import "./sheet.less";

View file

@ -1,4 +0,0 @@
@import "./sheet.less";
@import "./creation-action-footer.less";
@import "./selections-container.less";
@import "./tab-navigation.less";

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1,2 +0,0 @@
@import './sheets.less';
@import './damage-reduction-container.less';

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1 +0,0 @@
@import './death-move-container.less';

View file

@ -1 +0,0 @@
@import './roll-selection.less';

View file

@ -1 +0,0 @@
@import './downtime-container.less';

View file

@ -0,0 +1,44 @@
h1 {
color: @color-text-emphatic;
font: 700 var(--font-size-24) var(--dh-font-subtitle);
text-align: center;
}
header {
--bar-color: light-dark(@dark-blue, @golden);
color: light-dark(@dark, @beige);
display: flex;
justify-content: center;
align-items: center;
&:not(:first-child) {
margin-top: var(--spacer-8);
}
span {
padding: 0 10px;
}
&:before {
content: ' ';
flex: 1;
height: 1px;
background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, var(--bar-color) 100%);
}
&:after {
content: ' ';
flex: 1;
height: 1px;
background: linear-gradient(90deg, var(--bar-color) 0%, rgba(0, 0, 0, 0) 100%);
}
}
img.portrait {
border-radius: 50%;
border: none;
object-fit: cover;
object-position: center top;
width: 2.5rem;
height: 2.5rem;
}

View file

@ -1,3 +1,8 @@
@import './sheet.less';
@import './initialization.less';
@import './main.less';
.daggerheart.dialog.dh-style.views.group-roll-dialog {
.window-content {
@import "./_common.less";
}
}
@import "./initialization.less";
@import "./main.less";

View file

@ -1,48 +0,0 @@
.daggerheart.dialog.dh-style.views.group-roll-dialog {
.window-content {
h1 {
color: @color-text-emphatic;
font: 700 var(--font-size-24) var(--dh-font-subtitle);
text-align: center;
}
header {
--bar-color: light-dark(@dark-blue, @golden);
color: light-dark(@dark, @beige);
display: flex;
justify-content: center;
align-items: center;
&:not(:first-child) {
margin-top: var(--spacer-8);
}
span {
padding: 0 10px;
}
&:before {
content: ' ';
flex: 1;
height: 1px;
background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, var(--bar-color) 100%);
}
&:after {
content: ' ';
flex: 1;
height: 1px;
background: linear-gradient(90deg, var(--bar-color) 0%, rgba(0, 0, 0, 0) 100%);
}
}
img.portrait {
border-radius: 50%;
border: none;
object-fit: cover;
object-position: center top;
width: 2.5rem;
height: 2.5rem;
}
}
}

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1,20 +1,42 @@
@import './actions/index.less';
@import './attribution/index.less';
@import './beastform/index.less';
@import './character-creation/index.less';
@import './character-reset/index.less';
@import './compendiumBrowserPackDialog/index.less';
@import './damage-reduction/index.less';
@import './damage-selection/index.less';
@import './death-move/index.less';
@import './dice-roll/index.less';
@import './downtime/index.less';
@import './group-roll-dialog/index.less';
@import './attribution/sheet.less';
@import './level-up/index.less';
@import './resource-dice/index.less';
@import './multiclass-choice/index.less';
@import './tag-team-dialog/index.less';
@import './image-select/index.less';
@import './item-transfer/index.less';
@import './settings/index.less';
@import './risk-it-all/index.less';
@import './resource-dice/sheet.less';
@import './actions/action-list.less';
@import './damage-selection/sheet.less';
@import './downtime/downtime-container.less';
@import './death-move/death-move-container.less';
@import './beastform/sheet.less';
@import './character-creation/creation-action-footer.less';
@import './character-creation/selections-container.less';
@import './character-creation/sheet.less';
@import './character-creation/tab-navigation.less';
@import './dice-roll/roll-selection.less';
@import './damage-reduction/damage-reduction-container.less';
@import './damage-reduction/sheets.less';
@import './multiclass-choice/sheet.less';
@import './tag-team-dialog/initialization.less';
@import './tag-team-dialog/sheet.less';
@import './group-roll-dialog/index.less';
@import './image-select/sheet.less';
@import './item-transfer/sheet.less';
@import './settings/change-currency-icon.less';
@import './risk-it-all/sheet.less';
@import './character-reset/sheet.less';
@import './compendiumBrowserPackDialog/sheet.less';

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1 +0,0 @@
@import './change-currency-icon.less';

View file

@ -1,2 +0,0 @@
@import './sheet.less';
@import './initialization.less';

View file

@ -1 +1 @@
@import './token-hud/index.less';
@import './token-hud/token-hud.less';

View file

@ -1 +0,0 @@
@import './token-hud.less';

View file

@ -1,3 +0,0 @@
@import './sheet.less';
@import './experiences.less';
@import './features.less';

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1,2 +0,0 @@
@import './adversaries.less';
@import './features.less';

View file

@ -1,4 +1,8 @@
@import './header.less';
@import './adversary-settings/index.less';
@import './character-settings/index.less';
@import './environment-settings/index.less';
@import './adversary-settings/sheet.less';
@import './adversary-settings/experiences.less';
@import './adversary-settings/features.less';
@import './character-settings/sheet.less';
@import './environment-settings/features.less';
@import './environment-settings/adversaries.less';

View file

@ -1 +0,0 @@
@import './actions.less';

View file

@ -1 +0,0 @@
@import './activeEffects.less';

View file

@ -1,7 +0,0 @@
@import './actor-sheet-shared.less';
@import './adversary/index.less';
@import './character/index.less';
@import './companion/index.less';
@import './environment/index.less';
@import './npc/index.less';
@import './party/index.less';

View file

@ -1,5 +1,22 @@
@import './activeEffects/index.less';
@import './actions/index.less';
@import './actors/index.less';
@import './items/index.less';
@import './rollTables/index.less';
@import './actions/actions.less';
@import './actors/actor-sheet-shared.less';
@import './actors/adversary/index.less';
@import './actors/character/index.less';
@import './actors/companion/index.less';
@import './actors/environment/index.less';
@import './actors/npc/index.less';
@import './actors/party/index.less';
@import './items/beastform.less';
@import './items/class.less';
@import './items/domain-card.less';
@import './items/feature.less';
@import './items/heritage.less';
@import './items/item-sheet-shared.less';
@import './rollTables/sheet.less';
@import './actions/actions.less';
@import './activeEffects/activeEffects.less';

View file

@ -1,6 +0,0 @@
@import './beastform.less';
@import './class.less';
@import './domain-card.less';
@import './feature.less';
@import './heritage.less';
@import './item-sheet-shared.less';

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1,10 +0,0 @@
@import './sheet.less';
@import './ability-use.less';
@import './action.less';
@import './chat.less';
@import './damage-summary.less';
@import './deathmoves.less';
@import './downtime.less';
@import './effect-summary.less';
@import './group-roll.less';
@import './refresh-message.less';

View file

@ -1,5 +0,0 @@
@import './combat-sidebar.less';
@import './combatant-controls.less';
@import './encounter-controls.less';
@import './spotlight-control.less';
@import './token-actions.less';

View file

@ -1,3 +0,0 @@
@import './sheet.less';
@import './countdown-edit.less';
@import './countdown.less';

View file

@ -1 +0,0 @@
@import './sheet.less';

View file

@ -1,11 +1,40 @@
@import './chat/index.less';
@import './combat-sidebar/index.less';
@import './countdown/index.less';
@import './effects-display/index.less';
@import './item-browser/index.less';
@import './ownership-selection/index.less';
@import './resources/index.less';
@import './scene-config/index.less';
@import './scene-navigation/index.less';
@import './settings/index.less';
@import './sidebar/index.less';
@import './chat/ability-use.less';
@import './chat/action.less';
@import './chat/chat.less';
@import './chat/damage-summary.less';
@import './chat/downtime.less';
@import './chat/effect-summary.less';
@import './chat/group-roll.less';
@import './chat/refresh-message.less';
@import './chat/deathmoves.less';
@import './chat/sheet.less';
@import './combat-sidebar/combat-sidebar.less';
@import './combat-sidebar/combatant-controls.less';
@import './combat-sidebar/encounter-controls.less';
@import './combat-sidebar/spotlight-control.less';
@import './combat-sidebar/token-actions.less';
@import './item-browser/item-browser.less';
@import './countdown/countdown.less';
@import './countdown/countdown-edit.less';
@import './countdown/sheet.less';
@import './ownership-selection/ownership-selection.less';
@import './resources/resources.less';
@import './settings/settings.less';
@import './settings/homebrew-settings/domains.less';
@import './settings/homebrew-settings/types.less';
@import './settings/homebrew-settings/resources.less';
@import './settings/appearance-settings/diceSoNice.less';
@import './sidebar/tabs.less';
@import './sidebar/daggerheartMenu.less';
@import './scene-config/scene-config.less';
@import './effects-display/sheet.less';
@import './scene-navigation/scene-navigation.less';

View file

@ -1 +0,0 @@
@import './item-browser.less';

View file

@ -1 +0,0 @@
@import './ownership-selection.less';

View file

@ -1 +0,0 @@
@import './resources.less';

View file

@ -1 +0,0 @@
@import './scene-config.less';

View file

@ -1 +0,0 @@
@import './scene-navigation.less';

View file

@ -1 +0,0 @@
@import './diceSoNice.less';

View file

@ -1,3 +0,0 @@
@import './domains.less';
@import './resources.less';
@import './types.less';

View file

@ -1,3 +0,0 @@
@import './settings.less';
@import './appearance-settings/index.less';
@import './homebrew-settings/index.less';

View file

@ -1,2 +0,0 @@
@import './daggerheartMenu.less';
@import './tabs.less';

View file

@ -1,4 +0,0 @@
@import './colors.less';
@import './fonts.less';
@import './mixin.less';
@import './spacing.less';

View file

@ -1 +0,0 @@
@import './autocomplete.less';

View file

@ -1,2 +1,10 @@
@import './autocomplete/index.less';
@import './tooltip/index.less';
@import './tooltip/sheet.less';
@import './tooltip/tooltip.less';
@import './tooltip/armorManagement.less';
@import './tooltip/battlepoints.less';
@import './tooltip/bordered-tooltip.less';
@import './tooltip/domain-cards.less';
@import './autocomplete/autocomplete.less';
@import './tooltip/resource-management.less';

View file

@ -1,7 +0,0 @@
@import './sheet.less';
@import './armorManagement.less';
@import './battlepoints.less';
@import './bordered-tooltip.less';
@import './domain-cards.less';
@import './resource-management.less';
@import './tooltip.less';

View file

@ -2,11 +2,9 @@
<header class="countdowns-header">
<i class="fa-solid fa-clock-rotate-left" inert></i>
<span class="window-title">{{localize "DAGGERHEART.UI.Countdowns.title"}}</span>
{{#if isGM}}
<a class="header-control" data-tooltip aria-label="DAGGERHEART.APPLICATIONS.CountdownEdit.editTitle" data-action="editCountdowns">
<i class="fa-solid fa-wrench" inert></i>
</a>
{{/if}}
<a class="header-control" data-tooltip aria-label="DAGGERHEART.UI.Countdowns.toggleIconMode" data-action="toggleViewMode">
<i class="fa-solid fa-down-left-and-up-right-to-center" inert></i>
</a>

View file

@ -82,7 +82,7 @@ function getMean(numbers) {
}
function getMedianAverageDeviation(numbers, { median }) {
const residuals = numbers.map(d => Math.abs(d - median));
const residuals = allDamage.map(d => Math.abs(d - median));
return getMedian(residuals);
}

View file

@ -1,70 +1,45 @@
import fs from 'fs';
import path from 'path';
import readline from 'readline';
import { isContainedPath, readEnvFile } from './util.mjs';
const projectRoot = path.resolve(import.meta.dirname, '../')
const { foundryRoot, dataPath } = readEnvFile();
const askQuestion = question => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
async function createFoundrySymlink() {
// If foundry already exists, exit and inform the user. This operation can't complete correctly otherwise.
// If the folder is empty, its fine. It may have failed due to perms
const foundryDestPath = path.join(projectRoot, 'foundry');
if (fs.existsSync(foundryDestPath) && fs.readdirSync(foundryDestPath).length) {
console.log('"foundry" folder already exists in this project');
return;
}
return new Promise(resolve =>
rl.question(question, answer => {
rl.close();
resolve(answer);
})
);
};
console.log('Creating "foundry" symlinks for types');
const installPath = await askQuestion('Enter your Foundry install path: ');
// Determine if it's an Electron install (nested structure)
const nested = fs.existsSync(path.join(installPath, 'resources', 'app'));
const fileRoot = nested ? path.join(installPath, 'resources', 'app') : installPath;
try {
await fs.promises.mkdir('foundry');
} catch (e) {
if (e.code !== 'EEXIST') throw e;
}
// JavaScript files
for (const p of ['client', 'common', 'tsconfig.json']) {
try {
await fs.promises.mkdir(foundryDestPath);
console.log('Root foundry folder created');
await fs.promises.symlink(path.join(fileRoot, p), path.join('foundry', p));
} catch (e) {
if (e.code !== 'EEXIST') throw e;
}
// JavaScript files
for (const p of ['client', 'common', 'tsconfig.json']) {
try {
await fs.promises.symlink(path.join(foundryRoot, p), path.join(foundryDestPath, p));
console.log(`${p} folder created`);
} catch (e) {
if (e.code !== 'EEXIST') throw e;
}
}
// Language files
try {
await fs.promises.symlink(path.join(foundryRoot, 'public', 'lang'), path.join(foundryDestPath, 'lang'));
console.log(`lang folder created`);
} catch (e) {
if (e.code !== 'EEXIST') throw e;
console.log(`lang folder already exists`);
}
}
async function createDaggerheartSymlink() {
if (isContainedPath(dataPath, projectRoot)) {
console.log('The Daggerheart project repo is in foundry data, so a symlink won\'t be created');
return;
}
const destination = path.join(dataPath, 'Data', 'systems', 'daggerheart');
if (fs.existsSync(destination)) {
console.log('A Daggerheart folder already exists in Foundry data');
return;
}
console.log('Creating Daggerheart symlink in the foundry systems folder')
try {
await fs.promises.symlink(projectRoot, destination);
console.log('Daggerheart system folder symlink created');
} catch (e) {
// Language files
try {
await fs.promises.symlink(path.join(fileRoot, 'public', 'lang'), path.join('foundry', 'lang'));
} catch (e) {
if (e.code !== 'EEXIST') throw e;
console.log(`Daggerheart system folder already exists`);
}
}
await createFoundrySymlink();
console.log(); // Add empty newline
await createDaggerheartSymlink();

18
tools/dev-setup.mjs Normal file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env node
import fs from 'fs';
const args = process.argv.slice(2);
const foundryPath = args.find(arg => arg.startsWith('--foundry-path='))?.split('=')[1];
const dataPath = args.find(arg => arg.startsWith('--data-path='))?.split('=')[1];
if (!foundryPath || !dataPath) {
console.log('Usage: npm run setup:dev -- --foundry-path="/path/to/foundry/main.js" --data-path="/path/to/data"');
process.exit(1);
}
const envContent = `FOUNDRY_MAIN_PATH=${foundryPath}
FOUNDRY_DATA_PATH=${dataPath}
`;
fs.writeFileSync('.env', envContent);
console.log(`✅ Development environment configured: ${foundryPath}, ${dataPath}`);

View file

@ -1,20 +0,0 @@
import globals from 'globals';
import { defineConfig, globalIgnores } from 'eslint/config';
import { stylisticRules } from '../eslint.config.mjs';
import stylistic from '@stylistic/eslint-plugin';
export default defineConfig([
globalIgnores(['foundry/**/*']),
{
files: ['**/*.{js,mjs,cjs}'],
plugins: {
'@stylistic': stylistic
},
languageOptions: { globals: globals.node },
rules: {
'no-undef': 'error',
'no-unused-vars': 0,
...stylisticRules
}
}
]);

View file

@ -1,10 +1,21 @@
#!/usr/bin/env node
import { spawn } from 'child_process';
import { readEnvFile } from './util.mjs';
import fs from 'fs';
// Load .env file params
const { foundryPath, dataPath } = readEnvFile();
// Load .env file if it exists
if (fs.existsSync('.env')) {
const envFile = fs.readFileSync('.env', 'utf8');
envFile.split('\n').forEach(line => {
const [key, value] = line.split('=');
if (key && value) {
process.env[key] = value;
}
});
}
// Set defaults if not in environment
const foundryPath = process.env.FOUNDRY_MAIN_PATH || '../../../../FoundryDev/main.js';
const dataPath = process.env.FOUNDRY_DATA_PATH || '../../../';
// Run the original command with proper environment
const args = ['rollup -c --watch', `node "\"${foundryPath}\"" --dataPath="${dataPath}" --noupnp`, 'gulp'];

View file

@ -1,39 +0,0 @@
import fs from 'fs';
import path from 'path';
export function readEnvFile() {
if (!fs.existsSync('.env')) {
console.error('No configured .env file. Copy .env.example to .env and configure it.');
process.exit();
}
const envFile = fs.readFileSync('.env', 'utf8');
envFile.split('\n').forEach(line => {
const [key, value] = line.split('=');
if (key && value) {
process.env[key] = value;
}
});
// Determine foundry path, handling if its an electron install (nested structure)
const foundryPath = path.normalize(process.env.FOUNDRY_MAIN_PATH);
const dataPath = path.normalize(process.env.FOUNDRY_DATA_PATH);
if (!foundryPath.endsWith(path.join('app', 'main.js'))) {
console.error('Configured FOUNDRY_MAIN_PATH is invalid, it must end with app/main.js');
process.exit();
}
if (/Data(\/|\\)?$/.test(dataPath) || !fs.existsSync(path.join(dataPath, 'Data'))) {
console.error('Configured FOUNDRY_DATA_PATH is incorrect. This must be a folder that contains "Data"');
}
return {
foundryPath,
foundryRoot: path.dirname(foundryPath),
dataPath
};
}
export function isContainedPath(parent, child) {
const relative = path.relative(parent, child);
return relative && !relative.startsWith('..') && !path.isAbsolute(relative);
}