diff --git a/.gitignore b/.gitignore
index 81593582..fb54d74c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ index.html
main.js
node_modules/
src/Main/index.d.ts
+src/Worker/index.d.ts
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 93524e65..296d4b0a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,11 +7,13 @@ To get up and running:
```
npm install # install dependencies
-npm run generate # generate ts type definitions for elm ports
+npm run generate # generate ts type definitions for the main app
+npm run generate-worker # generate ts type definitions for the worker app
npm run dev # run dev build
```
## Release Checklist
+- run ts generators
- elm-review
- elm-test
- elm-coverage
diff --git a/README.md b/README.md
index 0decb2f0..808bdc21 100644
--- a/README.md
+++ b/README.md
@@ -15,10 +15,9 @@ An [Obsidian](https://obsidian.md/) plugin to make working with tasks a pleasure
- Tag based (use `#tags` to define columns).
## New
-- Due dates can be edited on the board - right click on a card
- to bring up the context menu. See the [Editing due date section](#editing-due-date).
-
-
+- No new features: I have split out some of the internal workings in preparation
+ for drag and drop - hopefully managed this without introducing any bugs or
+ performance issues.
## Installation
Please install via the regular Community Plugins setting tab within Obsidian.
diff --git a/TODO.md b/TODO.md
index f474708d..c4ad5b39 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,8 +1,45 @@
+- column sorting
+ - title, due date, path
+ - do I want to introduce priority at this point?
+
+STEP 3
+------
+- V: maintains (via W) file(s) to hold the order of cards
+ W needs to maintain the files as it needs keeping up to date
+ irrespcetive of whether the view is active
+ V needs to read the file at startup so it puts cards in the right places
+ if user DnDs then V sends postion changes to W which writes them to the file
+ hmmmmmmm
+
+
+- compare somne different forms of taskItem serialisation
+ - basic TsEncode/Decode
+ - can I make the taskItem encoding a bit more compact -> specially encoding Union types!
+ - original strings and parse to decode
+- TaskItemFields -> make alphabetical (contents is at the end atm)
+- perhaps when I am removing taskItems from the view I could request a reload of all if
+ the taskItem I am deleting doesn't exist
+- howabout when adding taskItems communicating the expected total and if this doesn't match
+ what the view has it can request a reload.
+- do I need the same states in the view now that I am not loading markdown?
+- typescipt tidying -> am I using async only where I need to?
+
+- can I make it so I load any settings at startup but save the latest version
+ when I get them back from elm?
+- do I want to deal with Settings (as in ensure they are the latest version) in the worker?
+- bug: if a file is deleted and it includes something which is shown in the completed
+ column then a blank item is added at the end as I do not render the markdown for
+ the newly added card.
+- I get an error in the js console if I click on the command icon when the
+ cardboard view is visible
+
---
- touch events - iPad ??
- show errors on settings pane ??
# Cleanups
+- do I need TaskItem.originalLine as it's the first line of originalBlock
+ would need a bit of work as there is no originalBlock for subtasks atm
- simplify parsing as per typing tutor
- replace regex stuff in TaskItem.toToggledSting with some form of token parsing
- if something is on a board because of a subtask tag and that line also has a due date on it, should I use
@@ -62,6 +99,7 @@
- undo buffer
- for toggling completion
- for deletion
+ - see: https://erkal.github.io/UndoRedo/index.html
- could/should I use some taskpaper tags:
@defer(date) - defer until date, e.g. 2016-04-19 5pm or next Thursday -3d
@estimate(time span) - time estimate, e.g. 2h for 2 hours or 3w for 3 weeks.
diff --git a/elm.json b/elm.json
index 0c5ef200..e8225aea 100644
--- a/elm.json
+++ b/elm.json
@@ -25,6 +25,7 @@
"justinmimbs/time-extra": "1.1.1",
"mpizenberg/elm-pointer-events": "5.0.0",
"pzp1997/assoc-list": "1.0.0",
+ "rluiten/stringdistance": "1.0.4",
"robinheghan/fnv1a": "1.0.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.4",
"tripokey/elm-fuzzy": "5.2.1"
@@ -42,12 +43,7 @@
"elm-explorations/test": "2.1.2"
},
"indirect": {
- "elm/project-metadata-utils": "1.0.2",
- "elm/random": "1.0.0",
- "miniBill/elm-unicode": "1.0.3",
- "rtfeldman/elm-hex": "1.0.0",
- "stil4m/elm-syntax": "7.3.2",
- "stil4m/structured-writer": "1.0.3"
+ "elm/random": "1.0.0"
}
}
}
diff --git a/package-lock.json b/package-lock.json
index 80d0d399..0c922bb4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "cardboard",
- "version": "0.7.5",
+ "version": "0.7.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cardboard",
- "version": "0.7.5",
+ "version": "0.7.8",
"license": "MIT",
"dependencies": {
"moment": "^2.29.4"
@@ -24,7 +24,7 @@
"elm-ts-interop": "^0.0.8",
"elm-webpack-loader": "^8.0.0",
"jest": "^29.7.0",
- "obsidian": "^1.4.11",
+ "obsidian": "^1.5.7",
"obsidian-daily-notes-interface": "^0.9.4",
"ts-jest": "^29.1.1",
"ts-loader": "^9.5.0",
@@ -32,7 +32,8 @@
"tslib": "^2.6.2",
"typescript": "^5.2.2",
"webpack": "^5.89.0",
- "webpack-cli": "^5.1.4"
+ "webpack-cli": "^5.1.4",
+ "webpack-merge": "^5.10.0"
}
},
"node_modules/@ampproject/remapping": {
@@ -207,9 +208,9 @@
}
},
"node_modules/@babel/core": {
- "version": "7.23.7",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz",
- "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==",
+ "version": "7.23.9",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz",
+ "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==",
"dev": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
@@ -217,11 +218,11 @@
"@babel/generator": "^7.23.6",
"@babel/helper-compilation-targets": "^7.23.6",
"@babel/helper-module-transforms": "^7.23.3",
- "@babel/helpers": "^7.23.7",
- "@babel/parser": "^7.23.6",
- "@babel/template": "^7.22.15",
- "@babel/traverse": "^7.23.7",
- "@babel/types": "^7.23.6",
+ "@babel/helpers": "^7.23.9",
+ "@babel/parser": "^7.23.9",
+ "@babel/template": "^7.23.9",
+ "@babel/traverse": "^7.23.9",
+ "@babel/types": "^7.23.9",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -236,15 +237,6 @@
"url": "https://opencollective.com/babel"
}
},
- "node_modules/@babel/core/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/@babel/generator": {
"version": "7.23.6",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz",
@@ -260,16 +252,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.20",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
- "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
- "dev": true,
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
"node_modules/@babel/helper-compilation-targets": {
"version": "7.23.6",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
@@ -286,30 +268,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "dev": true,
- "dependencies": {
- "yallist": "^3.0.2"
- }
- },
- "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true
- },
"node_modules/@babel/helper-environment-visitor": {
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
@@ -436,14 +394,14 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.23.7",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.7.tgz",
- "integrity": "sha512-6AMnjCoC8wjqBzDHkuqpa7jAKwvMo4dC+lr/TFBz+ucfulO1XMpDnwWPGBNwClOKZ8h6xn5N81W/R5OrcKtCbQ==",
+ "version": "7.23.9",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz",
+ "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==",
"dev": true,
"dependencies": {
- "@babel/template": "^7.22.15",
- "@babel/traverse": "^7.23.7",
- "@babel/types": "^7.23.6"
+ "@babel/template": "^7.23.9",
+ "@babel/traverse": "^7.23.9",
+ "@babel/types": "^7.23.9"
},
"engines": {
"node": ">=6.9.0"
@@ -535,9 +493,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.23.6",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz",
- "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==",
+ "version": "7.23.9",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz",
+ "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
@@ -724,23 +682,23 @@
}
},
"node_modules/@babel/template": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
- "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
+ "version": "7.23.9",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz",
+ "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==",
"dev": true,
"dependencies": {
- "@babel/code-frame": "^7.22.13",
- "@babel/parser": "^7.22.15",
- "@babel/types": "^7.22.15"
+ "@babel/code-frame": "^7.23.5",
+ "@babel/parser": "^7.23.9",
+ "@babel/types": "^7.23.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.23.7",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz",
- "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==",
+ "version": "7.23.9",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz",
+ "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.23.5",
@@ -749,8 +707,8 @@
"@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/parser": "^7.23.6",
- "@babel/types": "^7.23.6",
+ "@babel/parser": "^7.23.9",
+ "@babel/types": "^7.23.9",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@@ -759,9 +717,9 @@
}
},
"node_modules/@babel/types": {
- "version": "7.23.6",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz",
- "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
+ "version": "7.23.9",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz",
+ "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==",
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.23.4",
@@ -779,21 +737,21 @@
"dev": true
},
"node_modules/@codemirror/state": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.0.tgz",
- "integrity": "sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA==",
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
+ "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==",
"dev": true,
"peer": true
},
"node_modules/@codemirror/view": {
- "version": "6.7.1",
- "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.7.1.tgz",
- "integrity": "sha512-kYtS+uqYw/q/0ytYxpkqE1JVuK5NsbmBklWYhwLFTKO9gVuTdh/kDEeZPKorbqHcJ+P+ucrhcsS1czVweOpT2g==",
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.24.1.tgz",
+ "integrity": "sha512-sBfP4rniPBRQzNakwuQEqjEuiJDWJyF2kqLLqij4WXRoVwPPJfjx966Eq3F7+OPQxDtMt/Q9MWLoZLWjeveBlg==",
"dev": true,
"peer": true,
"dependencies": {
- "@codemirror/state": "^6.1.4",
- "style-mod": "^4.0.0",
+ "@codemirror/state": "^6.4.0",
+ "style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
@@ -809,6 +767,16 @@
"node": ">=12"
}
},
+ "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
@@ -1170,46 +1138,6 @@
}
}
},
- "node_modules/@jest/reporters/node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.20",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
- "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
- "dev": true,
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@jest/reporters/node_modules/jest-worker": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
- "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
- "dev": true,
- "dependencies": {
- "@types/node": "*",
- "jest-util": "^29.7.0",
- "merge-stream": "^2.0.0",
- "supports-color": "^8.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/reporters/node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
- }
- },
"node_modules/@jest/schemas": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
@@ -1236,16 +1164,6 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@jest/source-map/node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.20",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
- "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
- "dev": true,
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
"node_modules/@jest/test-result": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
@@ -1302,16 +1220,6 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/@jest/transform/node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.20",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
- "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
- "dev": true,
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
"node_modules/@jest/types": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
@@ -1330,9 +1238,9 @@
}
},
"node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
- "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz",
+ "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==",
"dev": true,
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
@@ -1344,9 +1252,9 @@
}
},
"node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
- "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"engines": {
"node": ">=6.0.0"
@@ -1372,19 +1280,19 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.4.14",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
- "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.9",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
- "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "version": "0.3.23",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz",
+ "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==",
"dev": true,
"dependencies": {
- "@jridgewell/resolve-uri": "^3.0.3",
- "@jridgewell/sourcemap-codec": "^1.4.10"
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@pkgjs/parseargs": {
@@ -1416,9 +1324,9 @@
}
},
"node_modules/@sinonjs/commons": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
- "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+ "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
"dev": true,
"dependencies": {
"type-detect": "4.0.8"
@@ -1464,9 +1372,9 @@
"dev": true
},
"node_modules/@tsconfig/node16": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
- "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true
},
"node_modules/@types/babel__core": {
@@ -1532,9 +1440,9 @@
}
},
"node_modules/@types/eslint": {
- "version": "8.4.10",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
- "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==",
+ "version": "8.56.3",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.3.tgz",
+ "integrity": "sha512-PvSf1wfv2wJpVIFUMSb+i4PvqNYkB9Rkp9ZDO3oaWzq4SKhsQk4mrMBr3ZH06I0hKrVGLBacmgl8JM4WVjb9dg==",
"dev": true,
"dependencies": {
"@types/estree": "*",
@@ -1542,9 +1450,9 @@
}
},
"node_modules/@types/eslint-scope": {
- "version": "3.7.4",
- "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
- "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
+ "version": "3.7.7",
+ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
+ "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"dev": true,
"dependencies": {
"@types/eslint": "*",
@@ -1552,9 +1460,9 @@
}
},
"node_modules/@types/estree": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
- "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
},
"node_modules/@types/graceful-fs": {
@@ -1567,39 +1475,39 @@
}
},
"node_modules/@types/http-cache-semantics": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
- "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
"dev": true
},
"node_modules/@types/istanbul-lib-coverage": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
- "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
+ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
"dev": true
},
"node_modules/@types/istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
+ "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
"dev": true,
"dependencies": {
"@types/istanbul-lib-coverage": "*"
}
},
"node_modules/@types/istanbul-reports": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
- "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
+ "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
"dev": true,
"dependencies": {
"@types/istanbul-lib-report": "*"
}
},
"node_modules/@types/jest": {
- "version": "29.5.8",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz",
- "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==",
+ "version": "29.5.12",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz",
+ "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==",
"dev": true,
"dependencies": {
"expect": "^29.0.0",
@@ -1607,9 +1515,9 @@
}
},
"node_modules/@types/json-schema": {
- "version": "7.0.11",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
- "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true
},
"node_modules/@types/keyv": {
@@ -1622,33 +1530,33 @@
}
},
"node_modules/@types/node": {
- "version": "20.9.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz",
- "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==",
+ "version": "20.11.20",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz",
+ "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/responselike": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
- "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
+ "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/stack-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
- "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
+ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
"dev": true
},
"node_modules/@types/tern": {
- "version": "0.23.7",
- "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.7.tgz",
- "integrity": "sha512-0YS9XCZ0LAhlP11HV9SqncUYyz9Ggsgc7Om/AmchKvoeFyj0qPaJmX6rJ93mJVExizWDzUMb49gAtVpI1uHd8Q==",
+ "version": "0.23.9",
+ "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
+ "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
"dev": true,
"dependencies": {
"@types/estree": "*"
@@ -1666,18 +1574,18 @@
}
},
"node_modules/@types/yargs": {
- "version": "17.0.18",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.18.tgz",
- "integrity": "sha512-eIJR1UER6ur3EpKM3d+2Pgd+ET+k6Kn9B4ZItX0oPjjVI5PrfaRjKyLT5UYendDpLuoiJMNJvovLQbEXqhsPaw==",
+ "version": "17.0.32",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
+ "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
"dev": true,
"dependencies": {
"@types/yargs-parser": "*"
}
},
"node_modules/@types/yargs-parser": {
- "version": "21.0.0",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
- "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
+ "version": "21.0.3",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
"dev": true
},
"node_modules/@webassemblyjs/ast": {
@@ -1883,9 +1791,9 @@
"dev": true
},
"node_modules/acorn": {
- "version": "8.11.2",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
- "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
@@ -1904,9 +1812,9 @@
}
},
"node_modules/acorn-walk": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
- "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
+ "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
"dev": true,
"engines": {
"node": ">=0.4.0"
@@ -2066,15 +1974,6 @@
"node": ">=8"
}
},
- "node_modules/babel-plugin-istanbul/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/babel-plugin-jest-hoist": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
@@ -2207,9 +2106,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.22.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz",
- "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
+ "version": "4.23.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+ "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
"dev": true,
"funding": [
{
@@ -2226,8 +2125,8 @@
}
],
"dependencies": {
- "caniuse-lite": "^1.0.30001565",
- "electron-to-chromium": "^1.4.601",
+ "caniuse-lite": "^1.0.30001587",
+ "electron-to-chromium": "^1.4.668",
"node-releases": "^2.0.14",
"update-browserslist-db": "^1.0.13"
},
@@ -2299,9 +2198,9 @@
}
},
"node_modules/cacheable-request": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
- "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==",
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
+ "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
"dev": true,
"dependencies": {
"clone-response": "^1.0.2",
@@ -2335,9 +2234,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001572",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz",
- "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==",
+ "version": "1.0.30001589",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz",
+ "integrity": "sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==",
"dev": true,
"funding": [
{
@@ -2380,16 +2279,10 @@
}
},
"node_modules/chokidar": {
- "version": "3.5.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
- "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
- "funding": [
- {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- ],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -2402,6 +2295,9 @@
"engines": {
"node": ">= 8.10.0"
},
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
"optionalDependencies": {
"fsevents": "~2.3.2"
}
@@ -2434,9 +2330,9 @@
}
},
"node_modules/ci-info": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz",
- "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==",
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
"dev": true,
"funding": [
{
@@ -2467,9 +2363,9 @@
}
},
"node_modules/cli-spinners": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
- "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==",
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
"dev": true,
"engines": {
"node": ">=6"
@@ -2650,15 +2546,15 @@
"dev": true
},
"node_modules/colorette": {
- "version": "2.0.19",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
- "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==",
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"dev": true
},
"node_modules/commander": {
- "version": "9.4.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz",
- "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==",
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
"dev": true,
"engines": {
"node": "^12.20.0 || >=14"
@@ -2848,9 +2744,9 @@
"dev": true
},
"node_modules/electron-to-chromium": {
- "version": "1.4.617",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.617.tgz",
- "integrity": "sha512-sYNE3QxcDS4ANW1k4S/wWYMXjCVcFSOX3Bg8jpuMFaXt/x8JCmp0R1Xe1ZXDX4WXnSRBf+GJ/3eGWicUuQq5cg==",
+ "version": "1.4.681",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.681.tgz",
+ "integrity": "sha512-1PpuqJUFWoXZ1E54m8bsLPVYwIVCRzvaL+n5cjigGga4z854abDnFRc+cTa2th4S79kyGqya/1xoR7h+Y5G5lg==",
"dev": true
},
"node_modules/elm": {
@@ -2944,70 +2840,6 @@
"url": "https://github.com/sponsors/jfmengels"
}
},
- "node_modules/elm-review/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/elm-review/node_modules/minimatch": {
- "version": "9.0.3",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
- "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/elm-review/node_modules/rimraf": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
- "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
- "dev": true,
- "dependencies": {
- "glob": "^10.3.7"
- },
- "bin": {
- "rimraf": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/elm-review/node_modules/rimraf/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
- "dev": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/elm-solve-deps-wasm": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/elm-solve-deps-wasm/-/elm-solve-deps-wasm-1.0.2.tgz",
@@ -3048,9 +2880,9 @@
}
},
"node_modules/elm-test/node_modules/glob": {
- "version": "8.0.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
- "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
@@ -3067,9 +2899,9 @@
}
},
"node_modules/elm-test/node_modules/minimatch": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz",
- "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==",
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -3191,9 +3023,9 @@
}
},
"node_modules/envinfo": {
- "version": "7.8.1",
- "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
- "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
+ "version": "7.11.1",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.1.tgz",
+ "integrity": "sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==",
"dev": true,
"bin": {
"envinfo": "dist/cli.js"
@@ -3218,9 +3050,9 @@
"dev": true
},
"node_modules/escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+ "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
"dev": true,
"engines": {
"node": ">=6"
@@ -3440,6 +3272,15 @@
"node": ">=6.4.0"
}
},
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
"node_modules/folder-hash": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/folder-hash/-/folder-hash-3.3.3.tgz",
@@ -3507,9 +3348,9 @@
"dev": true
},
"node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
@@ -3521,10 +3362,13 @@
}
},
"node_modules/function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
- "dev": true
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
@@ -3658,18 +3502,6 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
- "node_modules/has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "dependencies": {
- "function-bind": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4.0"
- }
- },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -3679,6 +3511,18 @@
"node": ">=8"
}
},
+ "node_modules/hasown": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
+ "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@@ -3805,12 +3649,12 @@
}
},
"node_modules/is-core-module": {
- "version": "2.11.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
- "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
+ "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
"dev": true,
"dependencies": {
- "has": "^1.0.3"
+ "hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -3934,14 +3778,14 @@
}
},
"node_modules/istanbul-lib-instrument": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz",
- "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz",
+ "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==",
"dev": true,
"dependencies": {
- "@babel/core": "^7.12.3",
- "@babel/parser": "^7.14.7",
- "@istanbuljs/schema": "^0.1.2",
+ "@babel/core": "^7.23.9",
+ "@babel/parser": "^7.23.9",
+ "@istanbuljs/schema": "^0.1.3",
"istanbul-lib-coverage": "^3.2.0",
"semver": "^7.5.4"
},
@@ -3949,38 +3793,71 @@
"node": ">=10"
}
},
- "node_modules/istanbul-lib-report": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
- "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+ "node_modules/istanbul-lib-instrument/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^4.0.0",
- "supports-color": "^7.1.0"
+ "yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
- "node_modules/istanbul-lib-source-maps": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
- "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+ "node_modules/istanbul-lib-instrument/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"dev": true,
"dependencies": {
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0",
- "source-map": "^0.6.1"
+ "lru-cache": "^6.0.0"
},
- "engines": {
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-instrument/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+ "dev": true,
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^4.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+ "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
"node": ">=10"
}
},
"node_modules/istanbul-reports": {
- "version": "3.1.6",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz",
- "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
+ "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
"dev": true,
"dependencies": {
"html-escaper": "^2.0.0",
@@ -4048,21 +3925,6 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/jest-changed-files/node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/jest-circus": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
@@ -4094,21 +3956,6 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/jest-circus/node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/jest-cli": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
@@ -4200,15 +4047,6 @@
"node": ">=12"
}
},
- "node_modules/jest-cli/node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "dev": true,
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/jest-config": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
@@ -4348,36 +4186,6 @@
"fsevents": "^2.3.2"
}
},
- "node_modules/jest-haste-map/node_modules/jest-worker": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
- "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
- "dev": true,
- "dependencies": {
- "@types/node": "*",
- "jest-util": "^29.7.0",
- "merge-stream": "^2.0.0",
- "supports-color": "^8.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-haste-map/node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
- }
- },
"node_modules/jest-leak-detector": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
@@ -4531,61 +4339,6 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/jest-runner/node_modules/jest-worker": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
- "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
- "dev": true,
- "dependencies": {
- "@types/node": "*",
- "jest-util": "^29.7.0",
- "merge-stream": "^2.0.0",
- "supports-color": "^8.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-runner/node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/jest-runner/node_modules/source-map-support": {
- "version": "0.5.13",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
- "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
- "dev": true,
- "dependencies": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
- "node_modules/jest-runner/node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
- }
- },
"node_modules/jest-runtime": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
@@ -4650,6 +4403,39 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
+ "node_modules/jest-snapshot/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
"node_modules/jest-util": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
@@ -4716,17 +4502,18 @@
}
},
"node_modules/jest-worker": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
- "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
"dev": true,
"dependencies": {
"@types/node": "*",
+ "jest-util": "^29.7.0",
"merge-stream": "^2.0.0",
"supports-color": "^8.0.0"
},
"engines": {
- "node": ">= 10.13.0"
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-worker/node_modules/supports-color": {
@@ -4818,9 +4605,9 @@
}
},
"node_modules/keyv": {
- "version": "4.5.2",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz",
- "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==",
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
"dependencies": {
"json-buffer": "3.0.1"
@@ -4944,15 +4731,12 @@
}
},
"node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
+ "yallist": "^3.0.2"
}
},
"node_modules/make-dir": {
@@ -4970,6 +4754,39 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/make-dir/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/make-dir/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@@ -5056,9 +4873,9 @@
}
},
"node_modules/minimist": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
- "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -5073,10 +4890,22 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
"node_modules/moment": {
- "version": "2.29.4",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
- "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+ "version": "2.30.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
}
@@ -5233,8 +5062,8 @@
}
},
"node_modules/obsidian": {
- "version": "1.4.11",
- "resolved": "git+ssh://git@github.com/obsidianmd/obsidian-api.git#791214a68d0dc322b88e5abce617bdf603cc2a2d",
+ "version": "1.5.7",
+ "resolved": "git+ssh://git@github.com/obsidianmd/obsidian-api.git#9754fc87dd9a417f198a5cad7f08959206d2f69c",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5265,6 +5094,15 @@
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==",
"dev": true
},
+ "node_modules/obsidian/node_modules/moment": {
+ "version": "2.29.4",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -5322,15 +5160,15 @@
}
},
"node_modules/p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"dependencies": {
- "p-try": "^2.0.0"
+ "yocto-queue": "^0.1.0"
},
"engines": {
- "node": ">=6"
+ "node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -5348,6 +5186,21 @@
"node": ">=8"
}
},
+ "node_modules/p-locate/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@@ -5425,13 +5278,10 @@
}
},
"node_modules/path-scurry/node_modules/lru-cache": {
- "version": "10.0.2",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.2.tgz",
- "integrity": "sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==",
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
+ "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
"dev": true,
- "dependencies": {
- "semver": "^7.3.5"
- },
"engines": {
"node": "14 || >=16.14"
}
@@ -5525,9 +5375,9 @@
}
},
"node_modules/punycode": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"engines": {
"node": ">=6"
@@ -5577,9 +5427,9 @@
"dev": true
},
"node_modules/readable-stream": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
- "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"dependencies": {
"inherits": "^2.0.3",
@@ -5630,12 +5480,12 @@
"dev": true
},
"node_modules/resolve": {
- "version": "1.22.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
- "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dev": true,
"dependencies": {
- "is-core-module": "^2.9.0",
+ "is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
@@ -5708,15 +5558,67 @@
}
},
"node_modules/rimraf": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+ "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
"dev": true,
"dependencies": {
- "glob": "^7.1.3"
+ "glob": "^10.3.7"
},
"bin": {
- "rimraf": "bin.js"
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "10.3.10",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+ "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+ "dev": true,
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^2.3.5",
+ "minimatch": "^9.0.1",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+ "path-scurry": "^1.10.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/safe-buffer": {
@@ -5758,24 +5660,18 @@
}
},
"node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
"bin": {
"semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
}
},
"node_modules/serialize-javascript": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
- "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
"dependencies": {
"randombytes": "^2.1.0"
@@ -5851,9 +5747,9 @@
}
},
"node_modules/source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
"dev": true,
"dependencies": {
"buffer-from": "^1.0.0",
@@ -5997,9 +5893,9 @@
}
},
"node_modules/style-mod": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz",
- "integrity": "sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz",
+ "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==",
"dev": true,
"peer": true
},
@@ -6062,16 +5958,16 @@
"node": ">=6.0.0"
}
},
- "node_modules/temp/node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "node_modules/temp/node_modules/rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"dependencies": {
- "minimist": "^1.2.6"
+ "glob": "^7.1.3"
},
"bin": {
- "mkdirp": "bin/cmd.js"
+ "rimraf": "bin.js"
}
},
"node_modules/terminal-link": {
@@ -6091,9 +5987,9 @@
}
},
"node_modules/terser": {
- "version": "5.24.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz",
- "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==",
+ "version": "5.28.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz",
+ "integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==",
"dev": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
@@ -6109,16 +6005,16 @@
}
},
"node_modules/terser-webpack-plugin": {
- "version": "5.3.9",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz",
- "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==",
+ "version": "5.3.10",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz",
+ "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==",
"dev": true,
"dependencies": {
- "@jridgewell/trace-mapping": "^0.3.17",
+ "@jridgewell/trace-mapping": "^0.3.20",
"jest-worker": "^27.4.5",
"schema-utils": "^3.1.1",
"serialize-javascript": "^6.0.1",
- "terser": "^5.16.8"
+ "terser": "^5.26.0"
},
"engines": {
"node": ">= 10.13.0"
@@ -6142,14 +6038,33 @@
}
}
},
- "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.20",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
- "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
+ "node_modules/terser-webpack-plugin/node_modules/jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"dev": true,
"dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/terser/node_modules/commander": {
@@ -6158,6 +6073,16 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
+ "node_modules/terser/node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
"node_modules/test-exclude": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
@@ -6206,9 +6131,9 @@
}
},
"node_modules/ts-jest": {
- "version": "29.1.1",
- "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz",
- "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==",
+ "version": "29.1.2",
+ "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz",
+ "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==",
"dev": true,
"dependencies": {
"bs-logger": "0.x",
@@ -6224,7 +6149,7 @@
"ts-jest": "cli.js"
},
"engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ "node": "^16.10.0 || ^18.0.0 || >=20.0.0"
},
"peerDependencies": {
"@babel/core": ">=7.0.0-beta.0 <8",
@@ -6248,19 +6173,43 @@
}
}
},
- "node_modules/ts-jest/node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "node_modules/ts-jest/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
"engines": {
- "node": ">=12"
+ "node": ">=10"
+ }
+ },
+ "node_modules/ts-jest/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
}
},
+ "node_modules/ts-jest/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
"node_modules/ts-loader": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.0.tgz",
- "integrity": "sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==",
+ "version": "9.5.1",
+ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz",
+ "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==",
"dev": true,
"dependencies": {
"chalk": "^4.1.0",
@@ -6277,6 +6226,33 @@
"webpack": "^5.0.0"
}
},
+ "node_modules/ts-loader/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ts-loader/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/ts-loader/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
@@ -6286,10 +6262,16 @@
"node": ">= 8"
}
},
+ "node_modules/ts-loader/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
"node_modules/ts-node": {
- "version": "10.9.1",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
- "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
@@ -6357,9 +6339,9 @@
}
},
"node_modules/typescript": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
- "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
+ "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -6376,9 +6358,9 @@
"dev": true
},
"node_modules/universalify": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
- "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"engines": {
"node": ">= 10.0.0"
@@ -6449,20 +6431,10 @@
"node": ">=10.12.0"
}
},
- "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.20",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
- "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
- "dev": true,
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
"node_modules/w3c-keyname": {
- "version": "2.2.6",
- "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz",
- "integrity": "sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==",
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
"dev": true,
"peer": true
},
@@ -6498,19 +6470,19 @@
}
},
"node_modules/webpack": {
- "version": "5.89.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
- "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
+ "version": "5.90.3",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz",
+ "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==",
"dev": true,
"dependencies": {
"@types/eslint-scope": "^3.7.3",
- "@types/estree": "^1.0.0",
+ "@types/estree": "^1.0.5",
"@webassemblyjs/ast": "^1.11.5",
"@webassemblyjs/wasm-edit": "^1.11.5",
"@webassemblyjs/wasm-parser": "^1.11.5",
"acorn": "^8.7.1",
"acorn-import-assertions": "^1.9.0",
- "browserslist": "^4.14.5",
+ "browserslist": "^4.21.10",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.15.0",
"es-module-lexer": "^1.2.1",
@@ -6524,7 +6496,7 @@
"neo-async": "^2.6.2",
"schema-utils": "^3.2.0",
"tapable": "^2.1.1",
- "terser-webpack-plugin": "^5.3.7",
+ "terser-webpack-plugin": "^5.3.10",
"watchpack": "^2.4.0",
"webpack-sources": "^3.2.3"
},
@@ -6599,12 +6571,13 @@
}
},
"node_modules/webpack-merge": {
- "version": "5.8.0",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
- "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
+ "version": "5.10.0",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz",
+ "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==",
"dev": true,
"dependencies": {
"clone-deep": "^4.0.1",
+ "flat": "^5.0.2",
"wildcard": "^2.0.0"
},
"engines": {
@@ -6636,15 +6609,15 @@
}
},
"node_modules/which-module": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
- "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
+ "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
"dev": true
},
"node_modules/wildcard": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
- "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
+ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
"dev": true
},
"node_modules/wrap-ansi": {
@@ -6714,9 +6687,9 @@
"dev": true
},
"node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},
"node_modules/yargs": {
@@ -6738,13 +6711,12 @@
}
},
"node_modules/yargs-parser": {
- "version": "13.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
- "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
- "dependencies": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
+ "engines": {
+ "node": ">=12"
}
},
"node_modules/yargs/node_modules/ansi-regex": {
@@ -6796,6 +6768,21 @@
"node": ">=6"
}
},
+ "node_modules/yargs/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/yargs/node_modules/p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
@@ -6843,6 +6830,16 @@
"node": ">=6"
}
},
+ "node_modules/yargs/node_modules/yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
diff --git a/package.json b/package.json
index 575fb761..782e99b4 100644
--- a/package.json
+++ b/package.json
@@ -4,9 +4,10 @@
"description": "Card based view for your obsidian Tasks",
"main": "main.js",
"scripts": {
- "build": "webpack --progress --color --mode=production",
- "dev": "webpack --mode=development --watch --progress --color",
+ "build": "webpack --progress --color --config webpack.prod.js",
+ "dev": "webpack --config webpack.dev.js --watch --progress --color",
"generate": "elm-ts-interop --definition-module InteropDefinitions --output src/Main/index.d.ts",
+ "generate-worker": "elm-ts-interop --definition-module Worker.InteropDefinitions --entrypoint Worker --output src/Worker/index.d.ts",
"test": "elm-test tests",
"test-ts": "jest"
},
@@ -26,7 +27,7 @@
"elm-ts-interop": "^0.0.8",
"elm-webpack-loader": "^8.0.0",
"jest": "^29.7.0",
- "obsidian": "^1.4.11",
+ "obsidian": "^1.5.7",
"obsidian-daily-notes-interface": "^0.9.4",
"ts-jest": "^29.1.1",
"ts-loader": "^9.5.0",
@@ -34,7 +35,8 @@
"tslib": "^2.6.2",
"typescript": "^5.2.2",
"webpack": "^5.89.0",
- "webpack-cli": "^5.1.4"
+ "webpack-cli": "^5.1.4",
+ "webpack-merge": "^5.10.0"
},
"dependencies": {
"moment": "^2.29.4"
diff --git a/src/DecodeHelpers.elm b/src/DecodeHelpers.elm
index e1caf2a6..582ed550 100644
--- a/src/DecodeHelpers.elm
+++ b/src/DecodeHelpers.elm
@@ -1,10 +1,25 @@
-module DecodeHelpers exposing (toElmVariant)
+module DecodeHelpers exposing
+ ( dateDecoder
+ , toElmVariant
+ , toElmVariant0
+ )
+import Date exposing (Date)
import Json.Encode as JE
import TsJson.Decode as TsDecode
+dateDecoder : TsDecode.Decoder Date
+dateDecoder =
+ TsDecode.map Date.fromRataDie TsDecode.int
+
+
toElmVariant : String -> (value -> a) -> TsDecode.Decoder value -> TsDecode.Decoder a
toElmVariant tagName constructor decoder_ =
TsDecode.field "tag" (TsDecode.literal constructor (JE.string tagName))
|> TsDecode.andMap (TsDecode.field "data" decoder_)
+
+
+toElmVariant0 : String -> a -> TsDecode.Decoder a
+toElmVariant0 tagName constructor =
+ TsDecode.field "tag" (TsDecode.literal constructor (JE.string tagName))
diff --git a/src/DueDate.elm b/src/DueDate.elm
index 45f2ac76..a771f5cf 100644
--- a/src/DueDate.elm
+++ b/src/DueDate.elm
@@ -1,9 +1,61 @@
-module DueDate exposing (DueDate(..))
+module DueDate exposing
+ ( DueDate(..)
+ , decoder
+ , encoder
+ )
import Date exposing (Date)
+import DecodeHelpers
+import EncodeHelpers
+import Json.Encode as JE
+import TsJson.Decode as TsDecode
+import TsJson.Encode as TsEncode
type DueDate
- = SetToDate Date
+ = NotSet
+ | SetToDate Date
| SetToNone
- | NotSet
+
+
+
+-- SERIALIZE
+
+
+decoder : TsDecode.Decoder DueDate
+decoder =
+ TsDecode.oneOf
+ [ DecodeHelpers.toElmVariant0 "NotSet" NotSet
+ , toElmSetToDate "SetToDate" SetToDate DecodeHelpers.dateDecoder
+ , DecodeHelpers.toElmVariant0 "SetToNone" SetToNone
+ ]
+
+
+encoder : TsEncode.Encoder DueDate
+encoder =
+ TsEncode.union
+ (\vNotSet vSetToDate vSetToNone value ->
+ case value of
+ NotSet ->
+ vNotSet
+
+ SetToDate date ->
+ vSetToDate date
+
+ SetToNone ->
+ vSetToNone
+ )
+ |> TsEncode.variant0 "NotSet"
+ |> TsEncode.variantObject "SetToDate" [ TsEncode.required "date" identity EncodeHelpers.dateEncoder ]
+ |> TsEncode.variant0 "SetToNone"
+ |> TsEncode.buildUnion
+
+
+
+-- PRIVATE
+
+
+toElmSetToDate : String -> (value -> a) -> TsDecode.Decoder value -> TsDecode.Decoder a
+toElmSetToDate tagName constructor decoder_ =
+ TsDecode.field "tag" (TsDecode.literal constructor (JE.string tagName))
+ |> TsDecode.andMap (TsDecode.field "date" decoder_)
diff --git a/src/EncodeHelpers.elm b/src/EncodeHelpers.elm
new file mode 100644
index 00000000..d1df6ae0
--- /dev/null
+++ b/src/EncodeHelpers.elm
@@ -0,0 +1,9 @@
+module EncodeHelpers exposing (dateEncoder)
+
+import Date exposing (Date)
+import TsJson.Encode as TsEncode
+
+
+dateEncoder : TsEncode.Encoder Date
+dateEncoder =
+ TsEncode.map Date.toRataDie TsEncode.int
diff --git a/src/InteropDefinitions.elm b/src/InteropDefinitions.elm
index d422dd9f..abac6cbf 100644
--- a/src/InteropDefinitions.elm
+++ b/src/InteropDefinitions.elm
@@ -2,14 +2,7 @@ module InteropDefinitions exposing
( Flags
, FromElm(..)
, ToElm(..)
- , addFilePreviewHoversEncoder
- , deleteTaskEncoder
- , displayTaskMarkdownEncoder
, interop
- , openTaskSourceFileEncoder
- , showCardContextMenuEncoder
- , trackDraggableEncoder
- , updateTasksEncoder
)
import DataviewTaskCompletion exposing (DataviewTaskCompletion)
@@ -17,8 +10,9 @@ import DecodeHelpers
import DragAndDrop.Coords as Coords exposing (Coords)
import DragAndDrop.DragData as DragData exposing (DragData)
import Filter exposing (Filter)
-import MarkdownFile exposing (MarkdownFile)
import Settings exposing (Settings)
+import TaskItem exposing (TaskItem, TaskItemFields)
+import TaskList exposing (TaskList)
import TextDirection exposing (TextDirection)
import TsJson.Decode as TsDecode
import TsJson.Encode as TsEncode exposing (required)
@@ -27,14 +21,15 @@ import TsJson.Encode as TsEncode exposing (required)
type FromElm
= AddFilePreviewHovers (List { filePath : String, id : String })
| CloseView
- | DeleteTask { filePath : String, lineNumber : Int, originalText : String }
+ | DeleteTask TaskItemFields
| DisplayTaskMarkdown (List { filePath : String, taskMarkdown : List { id : String, markdown : String } })
- | ElmInitialized
- | OpenTaskSourceFile { filePath : String, lineNumber : Int, originalText : String }
+ | ElmInitialized String
+ | OpenTaskSourceFile TaskItemFields
| RequestFilterCandidates
| ShowCardContextMenu { clientPos : ( Float, Float ), cardId : String }
| TrackDraggable { dragType : String, clientPos : Coords, draggableId : String }
- | UpdateTasks { filePath : String, tasks : List { lineNumber : Int, originalText : String, newText : String } }
+ | UpdateSettings Settings
+ | UpdateTasks { filePath : String, tasks : List { lineNumber : Int, originalLine : String, newText : String } }
type ToElm
@@ -42,14 +37,14 @@ type ToElm
| ConfigChanged TextDirection
| EditCardDueDate String
| ElementDragged DragData
- | FileAdded MarkdownFile
- | FileDeleted String
- | FileRenamed ( String, String )
- | FileUpdated MarkdownFile
| FilterCandidates (List Filter)
- | AllMarkdownLoaded
| SettingsUpdated Settings
| ShowBoard Int
+ | TaskItemsAdded TaskList
+ | TaskItemsDeleted (List String)
+ | TaskItemsDeletedAndAdded ( List String, List TaskItem )
+ | TaskItemsRefreshed TaskList
+ | TaskItemsUpdated (List ( String, TaskItem ))
type alias Flags =
@@ -72,73 +67,6 @@ interop =
--- ENCODERS
-
-
-addFilePreviewHoversEncoder : TsEncode.Encoder (List { filePath : String, id : String })
-addFilePreviewHoversEncoder =
- TsEncode.list
- (TsEncode.object
- [ required "filePath" .filePath TsEncode.string
- , required "id" .id TsEncode.string
- ]
- )
-
-
-deleteTaskEncoder : TsEncode.Encoder { a | filePath : String, lineNumber : Int, originalText : String }
-deleteTaskEncoder =
- TsEncode.object
- [ required "filePath" .filePath TsEncode.string
- , required "lineNumber" .lineNumber TsEncode.int
- , required "originalText" .originalText TsEncode.string
- ]
-
-
-displayTaskMarkdownEncoder : TsEncode.Encoder (List { filePath : String, taskMarkdown : List { id : String, markdown : String } })
-displayTaskMarkdownEncoder =
- TsEncode.list
- (TsEncode.object
- [ required "filePath" .filePath TsEncode.string
- , required "taskMarkdown" .taskMarkdown (TsEncode.list markdownListEncoder)
- ]
- )
-
-
-openTaskSourceFileEncoder : TsEncode.Encoder { a | filePath : String, lineNumber : Int, originalText : String }
-openTaskSourceFileEncoder =
- TsEncode.object
- [ required "filePath" .filePath TsEncode.string
- , required "lineNumber" .lineNumber TsEncode.int
- , required "originalText" .originalText TsEncode.string
- ]
-
-
-showCardContextMenuEncoder : TsEncode.Encoder { a | clientPos : ( Float, Float ), cardId : String }
-showCardContextMenuEncoder =
- TsEncode.object
- [ required "clientPos" .clientPos (TsEncode.tuple TsEncode.float TsEncode.float)
- , required "cardId" .cardId TsEncode.string
- ]
-
-
-trackDraggableEncoder : TsEncode.Encoder { dragType : String, clientPos : Coords, draggableId : String }
-trackDraggableEncoder =
- TsEncode.object
- [ required "dragType" .dragType TsEncode.string
- , required "clientPos" .clientPos Coords.encoder
- , required "draggableId" .draggableId TsEncode.string
- ]
-
-
-updateTasksEncoder : TsEncode.Encoder { filePath : String, tasks : List { lineNumber : Int, originalText : String, newText : String } }
-updateTasksEncoder =
- TsEncode.object
- [ required "filePath" .filePath TsEncode.string
- , required "tasks" .tasks (TsEncode.list taskUpdatesEncoder)
- ]
-
-
-
-- INTEROP
@@ -161,21 +89,21 @@ toElm =
, DecodeHelpers.toElmVariant "configChanged" ConfigChanged configChangedDecoder
, DecodeHelpers.toElmVariant "editCardDueDate" EditCardDueDate TsDecode.string
, DecodeHelpers.toElmVariant "elementDragged" ElementDragged DragData.decoder
- , DecodeHelpers.toElmVariant "fileAdded" FileAdded MarkdownFile.decoder
- , DecodeHelpers.toElmVariant "fileDeleted" FileDeleted TsDecode.string
- , DecodeHelpers.toElmVariant "fileRenamed" FileRenamed renamedFileDecoder
- , DecodeHelpers.toElmVariant "fileUpdated" FileUpdated MarkdownFile.decoder
, DecodeHelpers.toElmVariant "filterCandidates" FilterCandidates (TsDecode.list Filter.decoder)
- , DecodeHelpers.toElmVariant "allMarkdownLoaded" (always AllMarkdownLoaded) (TsDecode.succeed ())
, DecodeHelpers.toElmVariant "settingsUpdated" SettingsUpdated Settings.decoder
, DecodeHelpers.toElmVariant "showBoard" ShowBoard TsDecode.int
+ , DecodeHelpers.toElmVariant "taskItemsAdded" TaskItemsAdded TaskList.decoder
+ , DecodeHelpers.toElmVariant "taskItemsDeleted" TaskItemsDeleted (TsDecode.list TsDecode.string)
+ , DecodeHelpers.toElmVariant "taskItemsDeletedAndAdded" TaskItemsDeletedAndAdded deleteAndAddDecoder
+ , DecodeHelpers.toElmVariant "taskItemsRefreshed" TaskItemsRefreshed TaskList.decoder
+ , DecodeHelpers.toElmVariant "taskItemsUpdated" TaskItemsUpdated updateDetailsDecoder
]
fromElm : TsEncode.Encoder FromElm
fromElm =
TsEncode.union
- (\vAddFilePreviewHovers vCloseView vDeleteTask vDisplayTaskMarkdown vElmInitialized vOpenTaskSourceFile vRequestPaths vShowCardContextMenu vTrackDraggable _ vUpdateTasks value ->
+ (\vAddFilePreviewHovers vCloseView vDeleteTask vDisplayTaskMarkdown vElmInitialized vOpenTaskSourceFile vRequestPaths vShowCardContextMenu vTrackDraggable vUpdateSettings vUpdateTasks value ->
case value of
AddFilePreviewHovers info ->
vAddFilePreviewHovers info
@@ -183,17 +111,17 @@ fromElm =
CloseView ->
vCloseView
- DeleteTask info ->
- vDeleteTask info
+ DeleteTask taskItemFields ->
+ vDeleteTask taskItemFields
DisplayTaskMarkdown info ->
vDisplayTaskMarkdown info
- ElmInitialized ->
- vElmInitialized
+ ElmInitialized uniqueId ->
+ vElmInitialized uniqueId
- OpenTaskSourceFile info ->
- vOpenTaskSourceFile info
+ OpenTaskSourceFile taskItemFields ->
+ vOpenTaskSourceFile taskItemFields
RequestFilterCandidates ->
vRequestPaths
@@ -204,6 +132,9 @@ fromElm =
TrackDraggable info ->
vTrackDraggable info
+ UpdateSettings settings ->
+ vUpdateSettings settings
+
UpdateTasks info ->
vUpdateTasks info
)
@@ -211,7 +142,7 @@ fromElm =
|> TsEncode.variant0 "closeView"
|> TsEncode.variantTagged "deleteTask" deleteTaskEncoder
|> TsEncode.variantTagged "displayTaskMarkdown" displayTaskMarkdownEncoder
- |> TsEncode.variant0 "elmInitialized"
+ |> TsEncode.variantTagged "elmInitialized" TsEncode.string
|> TsEncode.variantTagged "openTaskSourceFile" openTaskSourceFileEncoder
|> TsEncode.variant0 "requestFilterCandidates"
|> TsEncode.variantTagged "showCardContextMenu" showCardContextMenuEncoder
@@ -225,12 +156,46 @@ fromElm =
-- HELPERS
+addFilePreviewHoversEncoder : TsEncode.Encoder (List { filePath : String, id : String })
+addFilePreviewHoversEncoder =
+ TsEncode.list
+ (TsEncode.object
+ [ required "filePath" .filePath TsEncode.string
+ , required "id" .id TsEncode.string
+ ]
+ )
+
+
configChangedDecoder : TsDecode.Decoder TextDirection
configChangedDecoder =
TsDecode.succeed TextDirection.fromRtlFlag
|> TsDecode.andMap (TsDecode.field "rightToLeft" TsDecode.bool)
+deleteAndAddDecoder : TsDecode.Decoder ( List String, List TaskItem )
+deleteAndAddDecoder =
+ TsDecode.tuple (TsDecode.list TsDecode.string) (TsDecode.list TaskItem.decoder)
+
+
+deleteTaskEncoder : TsEncode.Encoder { a | filePath : String, lineNumber : Int, originalLine : String }
+deleteTaskEncoder =
+ TsEncode.object
+ [ required "filePath" .filePath TsEncode.string
+ , required "lineNumber" .lineNumber TsEncode.int
+ , required "originalLine" .originalLine TsEncode.string
+ ]
+
+
+displayTaskMarkdownEncoder : TsEncode.Encoder (List { filePath : String, taskMarkdown : List { id : String, markdown : String } })
+displayTaskMarkdownEncoder =
+ TsEncode.list
+ (TsEncode.object
+ [ required "filePath" .filePath TsEncode.string
+ , required "taskMarkdown" .taskMarkdown (TsEncode.list markdownListEncoder)
+ ]
+ )
+
+
markdownListEncoder : TsEncode.Encoder { id : String, markdown : String }
markdownListEncoder =
TsEncode.object
@@ -239,17 +204,49 @@ markdownListEncoder =
]
-renamedFileDecoder : TsDecode.Decoder ( String, String )
-renamedFileDecoder =
- TsDecode.succeed Tuple.pair
- |> TsDecode.andMap (TsDecode.field "oldPath" TsDecode.string)
- |> TsDecode.andMap (TsDecode.field "newPath" TsDecode.string)
+openTaskSourceFileEncoder : TsEncode.Encoder { a | filePath : String, lineNumber : Int, originalLine : String }
+openTaskSourceFileEncoder =
+ TsEncode.object
+ [ required "filePath" .filePath TsEncode.string
+ , required "lineNumber" .lineNumber TsEncode.int
+ , required "originalLine" .originalLine TsEncode.string
+ ]
-taskUpdatesEncoder : TsEncode.Encoder { lineNumber : Int, originalText : String, newText : String }
+showCardContextMenuEncoder : TsEncode.Encoder { a | clientPos : ( Float, Float ), cardId : String }
+showCardContextMenuEncoder =
+ TsEncode.object
+ [ required "clientPos" .clientPos (TsEncode.tuple TsEncode.float TsEncode.float)
+ , required "cardId" .cardId TsEncode.string
+ ]
+
+
+taskUpdatesEncoder : TsEncode.Encoder { lineNumber : Int, originalLine : String, newText : String }
taskUpdatesEncoder =
TsEncode.object
[ required "lineNumber" .lineNumber TsEncode.int
- , required "originalText" .originalText TsEncode.string
+ , required "originalLine" .originalLine TsEncode.string
, required "newText" .newText TsEncode.string
]
+
+
+updateDetailsDecoder : TsDecode.Decoder (List ( String, TaskItem ))
+updateDetailsDecoder =
+ TsDecode.list <| TsDecode.tuple TsDecode.string TaskItem.decoder
+
+
+trackDraggableEncoder : TsEncode.Encoder { dragType : String, clientPos : Coords, draggableId : String }
+trackDraggableEncoder =
+ TsEncode.object
+ [ required "dragType" .dragType TsEncode.string
+ , required "clientPos" .clientPos Coords.encoder
+ , required "draggableId" .draggableId TsEncode.string
+ ]
+
+
+updateTasksEncoder : TsEncode.Encoder { filePath : String, tasks : List { lineNumber : Int, originalLine : String, newText : String } }
+updateTasksEncoder =
+ TsEncode.object
+ [ required "filePath" .filePath TsEncode.string
+ , required "tasks" .tasks (TsEncode.list taskUpdatesEncoder)
+ ]
diff --git a/src/InteropPorts.elm b/src/InteropPorts.elm
index 34a8b434..9380154b 100644
--- a/src/InteropPorts.elm
+++ b/src/InteropPorts.elm
@@ -20,11 +20,27 @@ import InteropDefinitions
import Json.Decode
import Json.Encode
import Settings exposing (Settings)
+import TaskItem exposing (TaskItemFields)
import TsJson.Decode as TsDecode
import TsJson.Encode as TsEncode
import UpdatedTaskItem exposing (UpdatedTaskItem)
+
+-- FLAGS
+
+
+decodeFlags : Json.Decode.Value -> Result Json.Decode.Error InteropDefinitions.Flags
+decodeFlags flags =
+ Json.Decode.decodeValue
+ (InteropDefinitions.interop.flags |> TsDecode.decoder)
+ flags
+
+
+
+-- SUBSCRIPTIONS
+
+
toElm : Sub (Result Json.Decode.Error InteropDefinitions.ToElm)
toElm =
(InteropDefinitions.interop.toElm |> TsDecode.decoder)
@@ -40,20 +56,23 @@ addHoverToCardEditButtons : List Card -> Cmd msg
addHoverToCardEditButtons cards =
cards
|> List.map (\c -> { filePath = Card.filePath c, id = Card.editButtonId c })
- |> encodeVariant "addFilePreviewHovers" InteropDefinitions.addFilePreviewHoversEncoder
+ |> InteropDefinitions.AddFilePreviewHovers
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
closeView : Cmd msg
closeView =
- encodeVariant "closeView" (TsEncode.object []) ()
+ InteropDefinitions.CloseView
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
-deleteTask : { a | filePath : String, lineNumber : Int, originalText : String } -> Cmd msg
-deleteTask info =
- info
- |> encodeVariant "deleteTask" InteropDefinitions.deleteTaskEncoder
+deleteTask : TaskItemFields -> Cmd msg
+deleteTask taskItemFields =
+ taskItemFields
+ |> InteropDefinitions.DeleteTask
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
@@ -61,88 +80,75 @@ displayTaskMarkdown : List Card -> Cmd msg
displayTaskMarkdown cards =
cards
|> List.map (\c -> { filePath = Card.filePath c, taskMarkdown = Card.markdownWithIds c })
- |> encodeVariant "displayTaskMarkdown" InteropDefinitions.displayTaskMarkdownEncoder
+ |> InteropDefinitions.DisplayTaskMarkdown
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
-elmInitialized : Cmd msg
-elmInitialized =
- encodeVariant "elmInitialized" (TsEncode.object []) ()
+elmInitialized : String -> Cmd msg
+elmInitialized uniqueId =
+ uniqueId
+ |> InteropDefinitions.ElmInitialized
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
-openTaskSourceFile : { a | filePath : String, lineNumber : Int, originalText : String } -> Cmd msg
-openTaskSourceFile info =
- info
- |> encodeVariant "openTaskSourceFile" InteropDefinitions.openTaskSourceFileEncoder
+openTaskSourceFile : TaskItemFields -> Cmd msg
+openTaskSourceFile taskItemFields =
+ taskItemFields
+ |> InteropDefinitions.OpenTaskSourceFile
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
requestFilterCandidates : Cmd msg
requestFilterCandidates =
- encodeVariant "requestFilterCandidates" (TsEncode.object []) ()
+ InteropDefinitions.RequestFilterCandidates
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
rewriteTasks : String -> List UpdatedTaskItem -> Cmd msg
rewriteTasks filePath updatedTaskItems =
let
- rewriteDetails : UpdatedTaskItem -> { lineNumber : Int, originalText : String, newText : String }
+ rewriteDetails : UpdatedTaskItem -> { lineNumber : Int, originalLine : String, newText : String }
rewriteDetails updatedTaskItem =
{ lineNumber = UpdatedTaskItem.lineNumber updatedTaskItem
- , originalText = UpdatedTaskItem.originalText updatedTaskItem
+ , originalLine = UpdatedTaskItem.originalLine updatedTaskItem
, newText = UpdatedTaskItem.toString updatedTaskItem
}
in
{ filePath = filePath, tasks = List.map rewriteDetails updatedTaskItems }
- |> encodeVariant "updateTasks" InteropDefinitions.updateTasksEncoder
+ |> InteropDefinitions.UpdateTasks
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
showCardContextMenu : ( Float, Float ) -> String -> Cmd msg
showCardContextMenu clientPos cardId =
{ clientPos = clientPos, cardId = cardId }
- |> encodeVariant "showCardContextMenu" InteropDefinitions.showCardContextMenuEncoder
+ |> InteropDefinitions.ShowCardContextMenu
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
trackDraggable : String -> Coords -> String -> Cmd msg
trackDraggable dragType clientPos draggableId =
{ dragType = dragType, clientPos = clientPos, draggableId = draggableId }
- |> encodeVariant "trackDraggable" InteropDefinitions.trackDraggableEncoder
+ |> InteropDefinitions.TrackDraggable
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
updateSettings : Settings -> Cmd msg
updateSettings settings =
{ settings | version = Settings.currentVersion }
- |> encodeVariant "updateSettings" Settings.encoder
+ |> InteropDefinitions.UpdateSettings
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
|> interopFromElm
--- HELPERS
-
-
-encodeVariant : String -> TsEncode.Encoder arg1 -> arg1 -> Json.Encode.Value
-encodeVariant variantName encoder_ arg1 =
- arg1
- |> (TsEncode.object
- [ TsEncode.required "tag" identity (TsEncode.literal (Json.Encode.string variantName))
- , TsEncode.required "data" identity encoder_
- ]
- |> TsEncode.encoder
- )
-
-
-decodeFlags : Json.Decode.Value -> Result Json.Decode.Error InteropDefinitions.Flags
-decodeFlags flags =
- Json.Decode.decodeValue
- (InteropDefinitions.interop.flags |> TsDecode.decoder)
- flags
-
-
-
-- PORTS
diff --git a/src/Main.elm b/src/Main.elm
index 66e7df48..eb3c538c 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -1,6 +1,5 @@
module Main exposing (Model, Msg, main)
-import BoardConfig
import Boards
import Browser
import Browser.Events as Browser
@@ -12,7 +11,6 @@ import Html exposing (Html)
import InteropDefinitions
import InteropPorts
import Json.Decode as JD
-import MarkdownFile exposing (MarkdownFile)
import Page
import Page.Board as BoardPage
import Page.Settings as SettingsPage
@@ -20,7 +18,7 @@ import SafeZipper
import Session exposing (Session)
import Settings exposing (Settings)
import Task
-import TaskItem
+import TaskItem exposing (TaskItem)
import TaskList exposing (TaskList)
import TextDirection exposing (TextDirection)
import Time exposing (Posix)
@@ -44,7 +42,7 @@ init flags =
( Settings (SettingsPage.init Session.default)
, Cmd.batch
[ Task.perform ReceiveTime <| Task.map2 Tuple.pair Time.here Time.now
- , InteropPorts.elmInitialized
+ , InteropPorts.elmInitialized ""
]
)
@@ -58,7 +56,7 @@ init flags =
|> forceAddWhenNoBoards
, Cmd.batch
[ InteropPorts.updateSettings <| Session.settings session
- , InteropPorts.elmInitialized
+ , InteropPorts.elmInitialized okFlags.uniqueId
, Task.perform ReceiveTime <| Task.map2 Tuple.pair Time.here Time.now
]
)
@@ -118,23 +116,23 @@ type KeyValue
type Msg
= ActiveStateUpdated Bool
- | AllMarkdownLoaded
| BadInputFromTypeScript
| ConfigChanged TextDirection
| EditCardDueDateRequested String
| ElementDragged DragData
- | SettingsUpdated Settings
| FilterCandidatesReceived (List Filter)
| GotBoardPageMsg BoardPage.Msg
| GotSettingsPageMsg SettingsPage.Msg
| KeyDown KeyValue
| ReceiveTime ( Time.Zone, Posix )
+ | SettingsUpdated Settings
| ShowBoard Int
| Tick Posix
- | VaultFileAdded MarkdownFile
- | VaultFileDeleted String
- | VaultFileRenamed ( String, String )
- | VaultFileUpdated MarkdownFile
+ | TaskItemsAdded TaskList
+ | TaskItemsDeletedAndAdded ( List String, List TaskItem )
+ | TaskItemsRefreshed TaskList
+ | TaskItemsDeleted (List String)
+ | TaskItemsUpdated (List ( String, TaskItem ))
update : Msg -> Model -> ( Model, Cmd Msg )
@@ -145,14 +143,6 @@ update msg model =
, Cmd.none
)
- ( AllMarkdownLoaded, _ ) ->
- ( mapSession Session.finishAdding model
- , Cmd.batch
- [ InteropPorts.displayTaskMarkdown <| Session.cards (toSession model)
- , InteropPorts.addHoverToCardEditButtons <| Session.cards (toSession model)
- ]
- )
-
( BadInputFromTypeScript, _ ) ->
( model, Cmd.none )
@@ -289,71 +279,46 @@ update msg model =
, cmd
)
- ( VaultFileAdded markdownFile, _ ) ->
- let
- newTasks : TaskList
- newTasks =
- TaskList.fromMarkdown (Session.dataviewTaskCompletion <| toSession model) markdownFile
-
- newModel : Model
- newModel =
- mapSession (\s -> Session.addTaskList newTasks s) model
- in
- ( newModel
- , cmdForTaskRedraws markdownFile.filePath (toSession newModel)
+ ( TaskItemsAdded taskList, _ ) ->
+ ( mapSession (Session.addTaskList taskList) model
+ , cmdForTaskRedraws taskList (toSession model)
)
- ( VaultFileDeleted filePath, _ ) ->
- ( mapSession (\s -> Session.deleteItemsFromFile filePath s) model
+ ( TaskItemsDeleted taskItems, _ ) ->
+ ( mapSession (Session.removeTaskItems taskItems) model
, Cmd.none
)
- ( VaultFileRenamed ( oldPath, newPath ), _ ) ->
+ ( TaskItemsDeletedAndAdded ( deleteIds, toAdd ), _ ) ->
let
- newModel : Model
- newModel =
- mapSession (Session.updatePath oldPath newPath) model
+ addList : TaskList
+ addList =
+ TaskList.fromList toAdd
in
- ( newModel
- , Cmd.batch
- [ cmdForTaskRedraws newPath (toSession newModel)
- , cmdForFilterPathRename newPath (toSession newModel)
- ]
+ ( model
+ |> mapSession (Session.removeTaskItems deleteIds)
+ |> mapSession (Session.addTaskList addList)
+ , cmdForTaskRedraws addList (toSession model)
)
- ( VaultFileUpdated markdownFile, _ ) ->
- let
- newTaskItems : TaskList
- newTaskItems =
- TaskList.fromMarkdown (Session.dataviewTaskCompletion <| toSession model) markdownFile
+ ( TaskItemsRefreshed taskList, _ ) ->
+ ( mapSession (Session.replaceTaskList taskList) model
+ , Cmd.none
+ )
- newModel : Model
- newModel =
- mapSession (\s -> Session.replaceTaskItems markdownFile.filePath newTaskItems s) model
+ ( TaskItemsUpdated updateDetails, _ ) ->
+ let
+ tasksToRedraw : TaskList
+ tasksToRedraw =
+ updateDetails
+ |> List.map Tuple.second
+ |> TaskList.fromList
in
- ( newModel
- , cmdForTaskRedraws markdownFile.filePath (toSession newModel)
+ ( mapSession (Session.replaceTaskItems updateDetails) model
+ , cmdForTaskRedraws tasksToRedraw (toSession model)
)
-cmdForFilterPathRename : String -> Session -> Cmd msg
-cmdForFilterPathRename newPath session =
- let
- anyUpdatedFilters : Bool
- anyUpdatedFilters =
- Session.boardConfigs session
- |> SafeZipper.toList
- |> List.concatMap BoardConfig.filters
- |> List.filter (\f -> Filter.filterType f == "Files" || Filter.filterType f == "Paths")
- |> List.any (\f -> Filter.value f == newPath)
- in
- if anyUpdatedFilters then
- InteropPorts.updateSettings <| Session.settings session
-
- else
- Cmd.none
-
-
cmdForDateChange : Session -> Cmd Msg
cmdForDateChange session =
let
@@ -379,8 +344,8 @@ cmdForDateChange session =
]
-cmdForTaskRedraws : String -> Session -> Cmd Msg
-cmdForTaskRedraws newPath session =
+cmdForTaskRedraws : TaskList -> Session -> Cmd Msg
+cmdForTaskRedraws taskList session =
let
today : Date
today =
@@ -389,8 +354,7 @@ cmdForTaskRedraws newPath session =
cards : List Card
cards =
- Session.taskList session
- |> TaskList.filter (\i -> TaskItem.filePath i == newPath)
+ taskList
|> Boards.init (Session.uniqueId session) (Session.boardConfigs session)
|> Boards.cards (Session.ignoreFileNameDates session) today
in
@@ -453,30 +417,30 @@ subscriptions _ =
InteropDefinitions.ElementDragged dragData ->
ElementDragged dragData
- InteropDefinitions.FileAdded markdownFile ->
- VaultFileAdded markdownFile
-
- InteropDefinitions.FileDeleted filePath ->
- VaultFileDeleted filePath
-
- InteropDefinitions.FileRenamed oldAndNewPath ->
- VaultFileRenamed oldAndNewPath
-
- InteropDefinitions.FileUpdated markdownFile ->
- VaultFileUpdated markdownFile
-
InteropDefinitions.FilterCandidates filterCandidates ->
FilterCandidatesReceived filterCandidates
- InteropDefinitions.AllMarkdownLoaded ->
- AllMarkdownLoaded
-
InteropDefinitions.SettingsUpdated newSettings ->
SettingsUpdated newSettings
InteropDefinitions.ShowBoard index ->
ShowBoard index
+ InteropDefinitions.TaskItemsAdded taskItems ->
+ TaskItemsAdded taskItems
+
+ InteropDefinitions.TaskItemsDeleted taskIds ->
+ TaskItemsDeleted taskIds
+
+ InteropDefinitions.TaskItemsDeletedAndAdded ( toDelete, toAdd ) ->
+ TaskItemsDeletedAndAdded ( toDelete, toAdd )
+
+ InteropDefinitions.TaskItemsRefreshed taskItems ->
+ TaskItemsRefreshed taskItems
+
+ InteropDefinitions.TaskItemsUpdated updateDetails ->
+ TaskItemsUpdated updateDetails
+
Err _ ->
BadInputFromTypeScript
)
diff --git a/src/Session.elm b/src/Session.elm
index 26f53583..1d293b86 100644
--- a/src/Session.elm
+++ b/src/Session.elm
@@ -7,7 +7,6 @@ module Session exposing
, cards
, dataviewTaskCompletion
, default
- , deleteItemsFromFile
, dragTracker
, findCard
, finishAdding
@@ -21,7 +20,9 @@ module Session exposing
, moveBoard
, moveColumn
, moveDragable
+ , removeTaskItems
, replaceTaskItems
+ , replaceTaskList
, settings
, stopTrackingDragable
, switchToBoardAt
@@ -370,19 +371,6 @@ addTaskList list ((Session config) as session) =
updateTaskListState (State.Loaded (TaskList.append currentList list)) session
-deleteItemsFromFile : String -> Session -> Session
-deleteItemsFromFile filePath ((Session config) as session) =
- case config.taskList of
- State.Waiting ->
- session
-
- State.Loading currentList ->
- updateTaskListState (State.Loading (TaskList.removeForFile filePath currentList)) session
-
- State.Loaded currentList ->
- updateTaskListState (State.Loaded (TaskList.removeForFile filePath currentList)) session
-
-
finishAdding : Session -> Session
finishAdding ((Session config) as session) =
case config.taskList of
@@ -396,17 +384,43 @@ finishAdding ((Session config) as session) =
session
-replaceTaskItems : String -> TaskList -> Session -> Session
-replaceTaskItems filePath updatedList ((Session config) as session) =
+removeTaskItems : List String -> Session -> Session
+removeTaskItems taskIds ((Session config) as session) =
case config.taskList of
State.Waiting ->
session
State.Loading currentList ->
- updateTaskListState (State.Loading (TaskList.replaceForFile filePath updatedList currentList)) session
+ updateTaskListState (State.Loading (TaskList.filter (\i -> not <| List.member (TaskItem.id i) taskIds) currentList)) session
State.Loaded currentList ->
- updateTaskListState (State.Loaded (TaskList.replaceForFile filePath updatedList currentList)) session
+ updateTaskListState (State.Loaded (TaskList.filter (\i -> not <| List.member (TaskItem.id i) taskIds) currentList)) session
+
+
+replaceTaskItems : List ( String, TaskItem ) -> Session -> Session
+replaceTaskItems updateDetails ((Session config) as session) =
+ case config.taskList of
+ State.Waiting ->
+ session
+
+ State.Loading currentList ->
+ updateTaskListState (State.Loading (TaskList.replaceTaskItems updateDetails currentList)) session
+
+ State.Loaded currentList ->
+ updateTaskListState (State.Loading (TaskList.replaceTaskItems updateDetails currentList)) session
+
+
+replaceTaskList : TaskList -> Session -> Session
+replaceTaskList newList ((Session config) as session) =
+ case config.taskList of
+ State.Waiting ->
+ updateTaskListState (State.Loaded newList) session
+
+ State.Loading _ ->
+ updateTaskListState (State.Loaded newList) session
+
+ State.Loaded _ ->
+ updateTaskListState (State.Loaded newList) session
updatePath : String -> String -> Session -> Session
diff --git a/src/Tag.elm b/src/Tag.elm
index 2aeada77..fd3c5ad9 100644
--- a/src/Tag.elm
+++ b/src/Tag.elm
@@ -1,6 +1,8 @@
module Tag exposing
( Tag
, containsInvalidCharacters
+ , decoder
+ , encoder
, equals
, matches
, parser
@@ -10,6 +12,8 @@ module Tag exposing
import Parser as P exposing ((|.), (|=), Parser)
import ParserHelper
+import TsJson.Decode as TsDecode
+import TsJson.Encode as TsEncode
import Unicode
@@ -36,6 +40,20 @@ parser =
+-- SERIALISE
+
+
+decoder : TsDecode.Decoder Tag
+decoder =
+ TsDecode.map Tag TsDecode.string
+
+
+encoder : TsEncode.Encoder Tag
+encoder =
+ TsEncode.map toString TsEncode.string
+
+
+
-- UTILITIES
diff --git a/src/TagList.elm b/src/TagList.elm
index 95ca4a54..41da119a 100644
--- a/src/TagList.elm
+++ b/src/TagList.elm
@@ -5,7 +5,9 @@ module TagList exposing
, containsTagMatching
, containsTagMatchingOneOf
, containsTagOtherThanThese
+ , decoder
, empty
+ , encoder
, filter
, fromList
, isEmpty
@@ -19,6 +21,8 @@ import List.Extra as LE
import Parser
import Set exposing (Set)
import Tag exposing (Tag)
+import TsJson.Decode as TsDecode
+import TsJson.Encode as TsEncode
@@ -62,6 +66,21 @@ cons tag (TagList tags) =
+-- SERIALISE
+
+
+decoder : TsDecode.Decoder TagList
+decoder =
+ TsDecode.list Tag.decoder
+ |> TsDecode.map TagList
+
+
+encoder : TsEncode.Encoder TagList
+encoder =
+ TsEncode.map toList <| TsEncode.list Tag.encoder
+
+
+
-- COMBINE
@@ -147,6 +166,11 @@ toStrings (TagList ts) =
-- PRIVATE
+toList : TagList -> List Tag
+toList (TagList ts) =
+ ts
+
+
toTags : TagList -> List Tag
toTags (TagList ts) =
ts
diff --git a/src/TaskItem.elm b/src/TaskItem.elm
index 62459563..5cc4c486 100644
--- a/src/TaskItem.elm
+++ b/src/TaskItem.elm
@@ -1,18 +1,22 @@
module TaskItem exposing
( AutoCompletion(..)
+ , CompareResult(..)
, Completion(..)
, Content
, TaskItem
, TaskItemFields
, allSubtasksWithMatchingTagCompleted
, asSingleTaskItems
+ , compare
, completedPosix
, completion
, containsId
+ , decoder
, descendantTasks
, due
, dueRataDie
, dummy
+ , encoder
, fields
, filePath
, hasNotes
@@ -27,7 +31,8 @@ module TaskItem exposing
, isFromFile
, lineNumber
, notes
- , originalText
+ , originalBlock
+ , originalLine
, parser
, removeFileNameDate
, removeMatchingTags
@@ -44,7 +49,9 @@ module TaskItem exposing
import DataviewDate
import DataviewTaskCompletion exposing (DataviewTaskCompletion)
import Date exposing (Date)
+import DecodeHelpers
import DueDate exposing (DueDate)
+import EncodeHelpers
import FNV1a
import Filter exposing (Filter, Scope)
import List.Extra as LE
@@ -52,10 +59,13 @@ import Maybe.Extra as ME
import ObsidianTasksDate
import Parser as P exposing ((|.), (|=), Parser)
import ParserHelper exposing (isSpaceOrTab, lineEndOrEnd)
+import StringDistance
import Tag exposing (Tag)
import TagList exposing (TagList)
import TaskPaperTag
import Time
+import TsJson.Decode as TsDecode
+import TsJson.Encode as TsEncode
@@ -65,15 +75,16 @@ import Time
type alias TaskItemFields =
{ autoComplete : AutoCompletion
, completion : Completion
+ , contents : List Content
, dueFile : Maybe Date
, dueTag : DueDate
, filePath : String
, lineNumber : Int
, notes : String
- , originalText : String
+ , originalBlock : String
+ , originalLine : String
, tags : TagList
, title : List String
- , contents : List Content
}
@@ -106,6 +117,14 @@ type IndentedItem
| Note String
+type CompareResult
+ = Different
+ | Identical
+ | Moved
+ | MovedAndUpdated
+ | Updated
+
+
dummy : TaskItem
dummy =
TaskItem defaultFields []
@@ -120,7 +139,8 @@ defaultFields =
, filePath = ""
, lineNumber = 0
, notes = ""
- , originalText = ""
+ , originalBlock = ""
+ , originalLine = ""
, tags = TagList.empty
, title = []
, contents = []
@@ -128,6 +148,34 @@ defaultFields =
+-- ENCODE / DECODE
+
+
+decoder : TsDecode.Decoder TaskItem
+decoder =
+ TsDecode.succeed TaskItem
+ |> TsDecode.andMap (TsDecode.field "fields" taskItemFieldsDecoder)
+ |> TsDecode.andMap (TsDecode.field "subFields" (TsDecode.list taskItemFieldsDecoder))
+
+
+encoder : TsEncode.Encoder TaskItem
+encoder =
+ let
+ foo : TaskItem -> { fields : TaskItemFields, subFields : List TaskItemFields }
+ foo (TaskItem fields_ subFields_) =
+ { fields = fields_
+ , subFields = subFields_
+ }
+ in
+ TsEncode.map foo
+ (TsEncode.object
+ [ TsEncode.required "fields" .fields taskItemFieldsEncoder
+ , TsEncode.required "subFields" .subFields (TsEncode.list taskItemFieldsEncoder)
+ ]
+ )
+
+
+
-- INFO
@@ -315,9 +363,14 @@ notes =
.notes << fields
-originalText : TaskItem -> String
-originalText =
- .originalText << fields
+originalBlock : TaskItem -> String
+originalBlock =
+ .originalBlock << fields
+
+
+originalLine : TaskItem -> String
+originalLine =
+ .originalLine << fields
tags : TaskItem -> TagList
@@ -423,6 +476,68 @@ titleWithTags taskItem =
+-- COMPARE
+
+
+compare : TaskItem -> TaskItem -> CompareResult
+compare other this =
+ let
+ otherTitle : String
+ otherTitle =
+ title other
+
+ sameBlock : Bool
+ sameBlock =
+ originalBlock this == originalBlock other
+
+ samePlace : Bool
+ samePlace =
+ id this == id other
+
+ sameTitle : Bool
+ sameTitle =
+ thisTitle == otherTitle
+
+ similarTitle : Float -> Bool
+ similarTitle threshold =
+ let
+ distance : Float
+ distance =
+ StringDistance.sift3Distance otherTitle thisTitle
+
+ maxLength : Float
+ maxLength =
+ toFloat <| max (String.length otherTitle) (String.length thisTitle)
+ in
+ distance / maxLength < threshold
+
+ thisTitle : String
+ thisTitle =
+ title this
+ in
+ if samePlace && sameBlock then
+ Identical
+
+ else if samePlace && similarTitle 0.25 then
+ Updated
+
+ else if samePlace && sameTitle then
+ Updated
+
+ else if not samePlace && sameBlock then
+ Moved
+
+ else if not samePlace && similarTitle 0.12 then
+ MovedAndUpdated
+
+ else if not samePlace && sameTitle then
+ MovedAndUpdated
+
+ else
+ Different
+
+
+
-- MODIFICATION
@@ -482,23 +597,11 @@ updateFilePath oldPath newPath ((TaskItem fields_ subtasks_) as taskItem) =
parser : DataviewTaskCompletion -> String -> Maybe String -> TagList -> Int -> Parser TaskItem
parser dataviewTaskCompletion pathToFile fileDate frontMatterTags bodyOffset =
- (P.succeed taskItemFieldsBuilder
+ P.succeed itemWithOriginalBlock
|= P.getOffset
- |= P.getCol
- |= P.succeed pathToFile
- |= P.succeed frontMatterTags
- |= P.succeed bodyOffset
- |= P.getRow
- |= prefixParser
- |. P.chompWhile isSpaceOrTab
- |= fileDateParser fileDate
- |= contentParser dataviewTaskCompletion
+ |= taskItemParser dataviewTaskCompletion pathToFile fileDate frontMatterTags bodyOffset
|= P.getOffset
- |. lineEndOrEnd
|= P.getSource
- )
- |> P.andThen rejectIfNoTitle
- |> P.andThen (addAnySubtasksAndNotes dataviewTaskCompletion pathToFile fileDate frontMatterTags bodyOffset)
@@ -620,6 +723,13 @@ indentedItemParser dataviewTaskCompletion pathToFile fileDate frontMatterTags bo
]
+itemWithOriginalBlock : Int -> TaskItem -> Int -> String -> TaskItem
+itemWithOriginalBlock startOffset (TaskItem fields_ subtasks_) endOffset source =
+ TaskItem
+ { fields_ | originalBlock = String.trimRight <| String.slice startOffset (endOffset - 0) source }
+ subtasks_
+
+
notesParser : Parser IndentedItem
notesParser =
P.succeed Note
@@ -743,13 +853,34 @@ taskItemFieldsBuilder startOffset startColumn path frontMatterTags bodyOffset ro
|> (\tif -> { tif | dueFile = dueFromFile })
|> (\tif -> { tif | filePath = path })
|> (\tif -> { tif | lineNumber = bodyOffset + row })
- |> (\tif -> { tif | originalText = sourceText })
+ |> (\tif -> { tif | originalLine = sourceText })
|> (\tif -> { tif | tags = TagList.append tif.tags frontMatterTags })
|> (\tif -> { tif | title = List.reverse tif.title })
|> (\tif -> { tif | contents = List.reverse contents })
|> addCompletionTime
+taskItemParser : DataviewTaskCompletion -> String -> Maybe String -> TagList -> Int -> Parser TaskItem
+taskItemParser dataviewTaskCompletion pathToFile fileDate frontMatterTags bodyOffset =
+ (P.succeed taskItemFieldsBuilder
+ |= P.getOffset
+ |= P.getCol
+ |= P.succeed pathToFile
+ |= P.succeed frontMatterTags
+ |= P.succeed bodyOffset
+ |= P.getRow
+ |= prefixParser
+ |. P.chompWhile isSpaceOrTab
+ |= fileDateParser fileDate
+ |= contentParser dataviewTaskCompletion
+ |= P.getOffset
+ |. lineEndOrEnd
+ |= P.getSource
+ )
+ |> P.andThen rejectIfNoTitle
+ |> P.andThen (addAnySubtasksAndNotes dataviewTaskCompletion pathToFile fileDate frontMatterTags bodyOffset)
+
+
toggleCompletion : { a | time : Time.Posix } -> TaskItem -> TaskItem
toggleCompletion timeWithZone (TaskItem fields_ subtasks_) =
case fields_.completion of
@@ -783,6 +914,103 @@ tokenParser dataviewTaskCompletion =
-- PRIVATE
+autoCompletionDecoder : TsDecode.Decoder AutoCompletion
+autoCompletionDecoder =
+ TsDecode.oneOf
+ [ DecodeHelpers.toElmVariant0 "FalseSpecified" FalseSpecified
+ , DecodeHelpers.toElmVariant0 "NotSpecifed" NotSpecifed
+ , DecodeHelpers.toElmVariant0 "TrueSpecified" TrueSpecified
+ ]
+
+
+autoCompletionEncoder : TsEncode.Encoder AutoCompletion
+autoCompletionEncoder =
+ TsEncode.union
+ (\vFalseSpecified vNotSpecifed vTrueSpecified value ->
+ case value of
+ FalseSpecified ->
+ vFalseSpecified
+
+ NotSpecifed ->
+ vNotSpecifed
+
+ TrueSpecified ->
+ vTrueSpecified
+ )
+ |> TsEncode.variant0 "FalseSpecified"
+ |> TsEncode.variant0 "NotSpecifed"
+ |> TsEncode.variant0 "TrueSpecified"
+ |> TsEncode.buildUnion
+
+
+completionDecoder : TsDecode.Decoder Completion
+completionDecoder =
+ TsDecode.oneOf
+ [ DecodeHelpers.toElmVariant0 "Completed" Completed
+ , DecodeHelpers.toElmVariant "CompletedAt" CompletedAt (TsDecode.map Time.millisToPosix TsDecode.int)
+ , DecodeHelpers.toElmVariant0 "Incomplete" Incomplete
+ ]
+
+
+completionEncoder : TsEncode.Encoder Completion
+completionEncoder =
+ TsEncode.union
+ (\vCompleted vCompletedAt vIncomplete value ->
+ case value of
+ Completed ->
+ vCompleted
+
+ CompletedAt timeStamp ->
+ vCompletedAt timeStamp
+
+ Incomplete ->
+ vIncomplete
+ )
+ |> TsEncode.variant0 "Completed"
+ |> TsEncode.variantTagged "CompletedAt" (TsEncode.map Time.posixToMillis TsEncode.int)
+ |> TsEncode.variant0 "Incomplete"
+ |> TsEncode.buildUnion
+
+
+contentDecoder : TsDecode.Decoder Content
+contentDecoder =
+ TsDecode.oneOf
+ [ DecodeHelpers.toElmVariant "AutoCompleteTag" AutoCompleteTag autoCompletionDecoder
+ , DecodeHelpers.toElmVariant "CompletedTag" CompletedTag (TsDecode.map Time.millisToPosix TsDecode.int)
+ , DecodeHelpers.toElmVariant "DueTag" DueTag DueDate.decoder
+ , DecodeHelpers.toElmVariant "ObsidianTag" ObsidianTag Tag.decoder
+ , DecodeHelpers.toElmVariant "Word" Word TsDecode.string
+ ]
+
+
+contentEncoder : TsEncode.Encoder Content
+contentEncoder =
+ TsEncode.union
+ (\vAutoCompleteTag vCompletedTag vDueTag vObsidianTag vWord value ->
+ case value of
+ AutoCompleteTag autoCompletion ->
+ vAutoCompleteTag autoCompletion
+
+ CompletedTag timeStamp ->
+ vCompletedTag timeStamp
+
+ DueTag date ->
+ vDueTag date
+
+ ObsidianTag tag ->
+ vObsidianTag tag
+
+ Word word ->
+ vWord word
+ )
+ |> TsEncode.variantTagged "AutoCompleteTag" autoCompletionEncoder
+ |> TsEncode.variantTagged "CompletedTag" (TsEncode.map Time.posixToMillis TsEncode.int)
+ |> TsEncode.variantTagged "DueTag" DueDate.encoder
+ |> TsEncode.variantTagged "ObsidianTag" Tag.encoder
+ |> TsEncode.variantTagged "Word" TsEncode.string
+ |> TsEncode.buildUnion
+
+
descendantTaskHasThisTag : String -> TaskItem -> Bool
descendantTaskHasThisTag tagToMatch =
let
@@ -807,6 +1035,41 @@ mapTags fn taskItem =
mapFields (\fs -> { fs | tags = fn fs.tags }) taskItem
+taskItemFieldsDecoder : TsDecode.Decoder TaskItemFields
+taskItemFieldsDecoder =
+ TsDecode.succeed TaskItemFields
+ |> TsDecode.andMap (TsDecode.field "autoComplete" autoCompletionDecoder)
+ |> TsDecode.andMap (TsDecode.field "completion" completionDecoder)
+ |> TsDecode.andMap (TsDecode.field "contents" (TsDecode.list contentDecoder))
+ |> TsDecode.andMap (TsDecode.field "dueFile" (TsDecode.maybe DecodeHelpers.dateDecoder))
+ |> TsDecode.andMap (TsDecode.field "dueTag" DueDate.decoder)
+ |> TsDecode.andMap (TsDecode.field "filePath" TsDecode.string)
+ |> TsDecode.andMap (TsDecode.field "lineNumber" TsDecode.int)
+ |> TsDecode.andMap (TsDecode.field "notes" TsDecode.string)
+ |> TsDecode.andMap (TsDecode.field "originalBlock" TsDecode.string)
+ |> TsDecode.andMap (TsDecode.field "originalLine" TsDecode.string)
+ |> TsDecode.andMap (TsDecode.field "tags" TagList.decoder)
+ |> TsDecode.andMap (TsDecode.field "title" (TsDecode.list TsDecode.string))
+
+
+taskItemFieldsEncoder : TsEncode.Encoder TaskItemFields
+taskItemFieldsEncoder =
+ TsEncode.object
+ [ TsEncode.required "autoComplete" .autoComplete autoCompletionEncoder
+ , TsEncode.required "completion" .completion completionEncoder
+ , TsEncode.required "contents" .contents (TsEncode.list contentEncoder)
+ , TsEncode.required "dueFile" .dueFile (TsEncode.maybe EncodeHelpers.dateEncoder)
+ , TsEncode.required "dueTag" .dueTag DueDate.encoder
+ , TsEncode.required "filePath" .filePath TsEncode.string
+ , TsEncode.required "lineNumber" .lineNumber TsEncode.int
+ , TsEncode.required "notes" .notes TsEncode.string
+ , TsEncode.required "originalBlock" .originalBlock TsEncode.string
+ , TsEncode.required "originalLine" .originalLine TsEncode.string
+ , TsEncode.required "tags" .tags TagList.encoder
+ , TsEncode.required "title" .title (TsEncode.list TsEncode.string)
+ ]
+
+
topLevelTaskHasThisTag : String -> TaskItem -> Bool
topLevelTaskHasThisTag tagToMatch =
let
diff --git a/src/TaskList.elm b/src/TaskList.elm
index 6104f25a..78462526 100644
--- a/src/TaskList.elm
+++ b/src/TaskList.elm
@@ -1,32 +1,38 @@
module TaskList exposing
( TaskList
+ , TaskListDiff
, add
, append
, concat
, containsTask
+ , decoder
, empty
+ , encoder
, filter
, foldl
+ , fromList
, fromMarkdown
, map
- , parser
- , removeForFile
- , replaceForFile
+ , markdownDiffs
+ , replaceTaskItems
, taskContainingId
, taskFromId
, taskIds
, taskTitles
- , tasks
+ , toList
, topLevelTasks
)
import DataviewTaskCompletion exposing (DataviewTaskCompletion)
+import Dict exposing (Dict)
import List.Extra as LE
import MarkdownFile exposing (MarkdownFile)
import Parser as P exposing (Parser)
import ParserHelper exposing (anyLineParser)
import TagList exposing (TagList)
import TaskItem exposing (TaskItem)
+import TsJson.Decode as TsDecode
+import TsJson.Encode as TsEncode
@@ -37,6 +43,12 @@ type TaskList
= TaskList (List TaskItem)
+type alias TaskListDiff =
+ { toAdd : List TaskItem
+ , toDelete : List TaskItem
+ }
+
+
-- CREATE
@@ -46,6 +58,11 @@ empty =
TaskList []
+fromList : List TaskItem -> TaskList
+fromList =
+ TaskList
+
+
fromMarkdown : DataviewTaskCompletion -> MarkdownFile -> TaskList
fromMarkdown dataviewTaskCompletion markdownFile =
P.run
@@ -66,13 +83,17 @@ add item (TaskList list) =
--- PARSE
+-- SERIALIZE
-parser : DataviewTaskCompletion -> String -> Maybe String -> TagList -> Int -> Parser TaskList
-parser dataviewTaskCompletion filePath fileDate frontMatterTags bodyOffset =
- P.loop [] (taskItemsHelp dataviewTaskCompletion filePath fileDate frontMatterTags bodyOffset)
- |> P.map (\ts -> TaskList ts)
+decoder : TsDecode.Decoder TaskList
+decoder =
+ TsDecode.map TaskList (TsDecode.list TaskItem.decoder)
+
+
+encoder : TsEncode.Encoder TaskList
+encoder =
+ TsEncode.map topLevelTasks (TsEncode.list TaskItem.encoder)
@@ -108,14 +129,20 @@ map fn =
TaskList << List.map fn << topLevelTasks
-replaceForFile : String -> TaskList -> TaskList -> TaskList
-replaceForFile filePath updatedList =
- append updatedList << removeForFile filePath
-
+replaceTaskItems : List ( String, TaskItem ) -> TaskList -> TaskList
+replaceTaskItems replacementDetails taskList =
+ let
+ idsToRemove : List String
+ idsToRemove =
+ List.map Tuple.first replacementDetails
-removeForFile : String -> TaskList -> TaskList
-removeForFile filePath =
- TaskList << itemsNotFromFile filePath << topLevelTasks
+ taskListToAdd : TaskList
+ taskListToAdd =
+ TaskList <| List.map Tuple.second replacementDetails
+ in
+ taskList
+ |> filter (\i -> not <| List.member (TaskItem.id i) idsToRemove)
+ |> append taskListToAdd
@@ -129,6 +156,38 @@ containsTask taskId taskList =
|> List.any (\ti -> TaskItem.id ti == taskId)
+markdownDiffs : DataviewTaskCompletion -> MarkdownFile -> TaskList -> TaskListDiff
+markdownDiffs dataviewTaskCompletion updatedMarkdown taskList =
+ let
+ updatedTasks : Dict String TaskItem
+ updatedTasks =
+ fromMarkdown dataviewTaskCompletion updatedMarkdown
+ |> topLevelTasks
+ |> List.map (\i -> ( TaskItem.id i ++ ":" ++ TaskItem.originalBlock i, i ))
+ |> Dict.fromList
+
+ existingTasks : Dict String TaskItem
+ existingTasks =
+ filter (TaskItem.isFromFile updatedMarkdown.filePath) taskList
+ |> topLevelTasks
+ |> List.map (\i -> ( TaskItem.id i ++ ":" ++ TaskItem.originalBlock i, i ))
+ |> Dict.fromList
+
+ toAdd : List TaskItem
+ toAdd =
+ Dict.diff updatedTasks existingTasks
+ |> Dict.toList
+ |> List.map Tuple.second
+
+ toRemove : List TaskItem
+ toRemove =
+ Dict.diff existingTasks updatedTasks
+ |> Dict.toList
+ |> List.map Tuple.second
+ in
+ TaskListDiff toAdd toRemove
+
+
taskTitles : TaskList -> List String
taskTitles =
List.map TaskItem.title << topLevelTasks
@@ -141,16 +200,16 @@ taskIds =
taskContainingId : String -> TaskList -> Maybe TaskItem
taskContainingId id =
- LE.find (TaskItem.containsId id) << tasks
+ LE.find (TaskItem.containsId id) << toList
taskFromId : String -> TaskList -> Maybe TaskItem
taskFromId id =
- LE.find (\i -> TaskItem.id i == id) << tasks
+ LE.find (\i -> TaskItem.id i == id) << toList
-tasks : TaskList -> List TaskItem
-tasks =
+toList : TaskList -> List TaskItem
+toList =
List.concatMap (\t -> t :: TaskItem.descendantTasks t) << topLevelTasks
@@ -163,9 +222,10 @@ topLevelTasks (TaskList taskList) =
-- PRIVATE
-itemsNotFromFile : String -> List TaskItem -> List TaskItem
-itemsNotFromFile pathToFile =
- List.filter (\t -> not (TaskItem.isFromFile pathToFile t))
+parser : DataviewTaskCompletion -> String -> Maybe String -> TagList -> Int -> Parser TaskList
+parser dataviewTaskCompletion filePath fileDate frontMatterTags bodyOffset =
+ P.loop [] (taskItemsHelp dataviewTaskCompletion filePath fileDate frontMatterTags bodyOffset)
+ |> P.map (\ts -> TaskList ts)
taskItemsHelp : DataviewTaskCompletion -> String -> Maybe String -> TagList -> Int -> List TaskItem -> Parser (P.Step (List TaskItem) (List TaskItem))
diff --git a/src/UpdatedTaskItem.elm b/src/UpdatedTaskItem.elm
index 7558954d..d8be9fb6 100644
--- a/src/UpdatedTaskItem.elm
+++ b/src/UpdatedTaskItem.elm
@@ -4,7 +4,7 @@ module UpdatedTaskItem exposing
, dueString
, init
, lineNumber
- , originalText
+ , originalLine
, toString
, toggleCompletion
, updateDate
@@ -106,9 +106,9 @@ lineNumber (UpdatedTaskItem _ taskItem) =
TaskItem.lineNumber taskItem
-originalText : UpdatedTaskItem -> String
-originalText (UpdatedTaskItem _ taskItem) =
- TaskItem.originalText taskItem
+originalLine : UpdatedTaskItem -> String
+originalLine (UpdatedTaskItem _ taskItem) =
+ TaskItem.originalLine taskItem
toString : UpdatedTaskItem -> String
@@ -153,7 +153,7 @@ toString ((UpdatedTaskItem change taskItem) as updatedTaskItem) =
>> dataviewRemover
in
updatedTaskItem
- |> originalText
+ |> originalLine
|> replaceCheckbox taskItem
|> removeCompletionTags
|> insertBeforeBlockLink (completionTag taskCompletionSettings now taskItem)
@@ -169,18 +169,18 @@ toString ((UpdatedTaskItem change taskItem) as updatedTaskItem) =
case newDate of
Nothing ->
updatedTaskItem
- |> originalText
+ |> originalLine
|> removeDueTags
|> insertBeforeBlockLink (noneTagIfHasFileDate taskItem)
Just date ->
updatedTaskItem
- |> originalText
+ |> originalLine
|> removeDueTags
|> insertBeforeBlockLink (dueTag taskCompletionSettings date taskItem)
NoChange ->
- originalText updatedTaskItem
+ originalLine updatedTaskItem
noneTagIfHasFileDate : TaskItem -> String
diff --git a/src/Worker.elm b/src/Worker.elm
new file mode 100644
index 00000000..e41d700d
--- /dev/null
+++ b/src/Worker.elm
@@ -0,0 +1,190 @@
+module Worker exposing (Model, Msg, main)
+
+import Json.Decode as JD
+import List.Extra as LE
+import MarkdownFile exposing (MarkdownFile)
+import TaskItem exposing (TaskItem)
+import TaskList exposing (TaskList, TaskListDiff)
+import Worker.InteropDefinitions as InteropDefinitions
+import Worker.InteropPorts as InteropPorts
+import Worker.Session as Session exposing (Session)
+
+
+main : Program JD.Value Model Msg
+main =
+ Platform.worker
+ { init = init
+ , update = update
+ , subscriptions = subscriptions
+ }
+
+
+type Model
+ = FlagsError Session
+ | Yeah Session
+
+
+init : JD.Value -> ( Model, Cmd Msg )
+init flags =
+ case flags |> InteropPorts.decodeFlags of
+ Err _ ->
+ ( FlagsError Session.default, Cmd.none )
+
+ Ok okFlags ->
+ let
+ session : Session
+ session =
+ Session.fromFlags okFlags
+ in
+ ( Yeah session
+ , Cmd.none
+ )
+
+
+toSession : Model -> Session
+toSession model =
+ case model of
+ FlagsError session ->
+ session
+
+ Yeah session ->
+ session
+
+
+mapSession : (Session -> Session) -> Model -> Model
+mapSession fn model =
+ case model of
+ FlagsError session ->
+ FlagsError <| fn session
+
+ Yeah session ->
+ Yeah <| fn session
+
+
+
+-- UPDATE
+
+
+type Msg
+ = AllMarkdownLoaded
+ | ViewInitialized
+ | BadInputFromTypeScript
+ | VaultFileAdded MarkdownFile
+ | VaultFileDeleted String
+ | VaultFileModified MarkdownFile
+ | VaultFileRenamed ( String, String )
+
+
+update : Msg -> Model -> ( Model, Cmd Msg )
+update msg model =
+ case msg of
+ AllMarkdownLoaded ->
+ ( mapSession Session.finishAdding model, InteropPorts.allTasksLoaded )
+
+ ViewInitialized ->
+ ( model, InteropPorts.allTaskItems <| Session.taskList <| toSession model )
+
+ BadInputFromTypeScript ->
+ ( model, Cmd.none )
+
+ VaultFileAdded markdownFile ->
+ let
+ newTasks : TaskList
+ newTasks =
+ TaskList.fromMarkdown (Session.dataviewTaskCompletion <| toSession model) markdownFile
+ in
+ ( mapSession (Session.addTaskList newTasks) model
+ , InteropPorts.tasksAdded newTasks
+ )
+
+ VaultFileDeleted filePath ->
+ let
+ ( toDelete, remaining ) =
+ toSession model
+ |> Session.taskList
+ |> TaskList.topLevelTasks
+ |> List.partition (TaskItem.isFromFile filePath)
+ in
+ ( mapSession (Session.replaceTaskList <| TaskList.fromList remaining) model
+ , InteropPorts.tasksDeleted toDelete
+ )
+
+ VaultFileModified markdownFile ->
+ let
+ deleteIds : List String
+ deleteIds =
+ List.map TaskItem.id taskListDiff.toDelete
+
+ session : Session
+ session =
+ toSession model
+
+ taskListDiff : TaskListDiff
+ taskListDiff =
+ TaskList.markdownDiffs
+ (Session.dataviewTaskCompletion session)
+ markdownFile
+ (Session.taskList session)
+ in
+ ( model
+ |> mapSession (Session.removeTaskItems deleteIds)
+ |> mapSession (Session.addTaskList <| TaskList.fromList taskListDiff.toAdd)
+ , InteropPorts.tasksDeletedAndAdded deleteIds taskListDiff.toAdd
+ )
+
+ VaultFileRenamed ( oldPath, newPath ) ->
+ let
+ originalIds : List String
+ originalIds =
+ List.map TaskItem.id toUpdate
+
+ updatedTaskItems : List TaskItem
+ updatedTaskItems =
+ List.map (TaskItem.updateFilePath oldPath newPath) toUpdate
+
+ ( toUpdate, remaining ) =
+ toSession model
+ |> Session.taskList
+ |> TaskList.topLevelTasks
+ |> List.partition (TaskItem.isFromFile oldPath)
+ in
+ ( mapSession (Session.replaceTaskList <| TaskList.fromList (remaining ++ updatedTaskItems)) model
+ , InteropPorts.tasksUpdated (LE.zip originalIds updatedTaskItems)
+ )
+
+
+
+-- SUBSCRIPTIONS
+
+
+subscriptions : Model -> Sub Msg
+subscriptions _ =
+ Sub.batch
+ [ InteropPorts.toElm
+ |> Sub.map
+ (\result ->
+ case result of
+ Ok toElm ->
+ case toElm of
+ InteropDefinitions.AllMarkdownLoaded ->
+ AllMarkdownLoaded
+
+ InteropDefinitions.ViewInitialized ->
+ ViewInitialized
+
+ InteropDefinitions.FileAdded markdownFile ->
+ VaultFileAdded markdownFile
+
+ InteropDefinitions.FileDeleted filePath ->
+ VaultFileDeleted filePath
+
+ InteropDefinitions.FileModified markdownFile ->
+ VaultFileModified markdownFile
+
+ InteropDefinitions.FileRenamed oldAndNewPath ->
+ VaultFileRenamed oldAndNewPath
+
+ Err _ ->
+ BadInputFromTypeScript
+ )
+ ]
diff --git a/src/Worker/InteropDefinitions.elm b/src/Worker/InteropDefinitions.elm
new file mode 100644
index 00000000..09e74e9f
--- /dev/null
+++ b/src/Worker/InteropDefinitions.elm
@@ -0,0 +1,124 @@
+module Worker.InteropDefinitions exposing
+ ( Flags
+ , FromElm(..)
+ , ToElm(..)
+ , interop
+ )
+
+import DataviewTaskCompletion exposing (DataviewTaskCompletion)
+import DecodeHelpers
+import MarkdownFile exposing (MarkdownFile)
+import TaskItem exposing (TaskItem)
+import TaskList exposing (TaskList)
+import TsJson.Decode as TsDecode
+import TsJson.Encode as TsEncode
+
+
+type FromElm
+ = AllTasksLoaded
+ | AllTaskItems TaskList
+ | TasksAdded TaskList
+ | TasksDeleted (List TaskItem)
+ | TasksDeletedAndAdded ( List String, List TaskItem )
+ | TasksUpdated (List ( String, TaskItem ))
+
+
+type ToElm
+ = AllMarkdownLoaded
+ | ViewInitialized
+ | FileAdded MarkdownFile
+ | FileDeleted String
+ | FileModified MarkdownFile
+ | FileRenamed ( String, String )
+
+
+type alias Flags =
+ { dataviewTaskCompletion : DataviewTaskCompletion }
+
+
+interop : { toElm : TsDecode.Decoder ToElm, fromElm : TsEncode.Encoder FromElm, flags : TsDecode.Decoder Flags }
+interop =
+ { toElm = toElm
+ , fromElm = fromElm
+ , flags = flags
+ }
+
+
+
+-- INTEROP
+
+
+flags : TsDecode.Decoder Flags
+flags =
+ TsDecode.succeed Flags
+ |> TsDecode.andMap (TsDecode.field "dataviewTaskCompletion" DataviewTaskCompletion.decoder)
+
+
+toElm : TsDecode.Decoder ToElm
+toElm =
+ TsDecode.oneOf
+ [ DecodeHelpers.toElmVariant "allMarkdownLoaded" (always AllMarkdownLoaded) (TsDecode.succeed ())
+ , DecodeHelpers.toElmVariant0 "viewInitialized" ViewInitialized
+ , DecodeHelpers.toElmVariant "fileAdded" FileAdded MarkdownFile.decoder
+ , DecodeHelpers.toElmVariant "fileDeleted" FileDeleted TsDecode.string
+ , DecodeHelpers.toElmVariant "fileModified" FileModified MarkdownFile.decoder
+ , DecodeHelpers.toElmVariant "fileRenamed" FileRenamed renamedFileDecoder
+ ]
+
+
+fromElm : TsEncode.Encoder FromElm
+fromElm =
+ TsEncode.union
+ (\vAllTasksLoaded vAllTaskItems vTasksAdded vTasksDeleted vTasksDeletedAndAdded vTasksUpdated value ->
+ case value of
+ AllTasksLoaded ->
+ vAllTasksLoaded
+
+ AllTaskItems taskList ->
+ vAllTaskItems taskList
+
+ TasksAdded taskList ->
+ vTasksAdded taskList
+
+ TasksDeleted taskItems ->
+ vTasksDeleted taskItems
+
+ TasksDeletedAndAdded toDeletedAndAdd ->
+ vTasksDeletedAndAdded toDeletedAndAdd
+
+ TasksUpdated tasksToUpdate ->
+ vTasksUpdated tasksToUpdate
+ )
+ |> TsEncode.variant0 "allTasksLoaded"
+ |> TsEncode.variantTagged "allTaskItems" TaskList.encoder
+ |> TsEncode.variantTagged "tasksAdded" TaskList.encoder
+ |> TsEncode.variantTagged "tasksDeleted" tasksDeletedEncoder
+ |> TsEncode.variantTagged "tasksDeletedAndAdded" tasksDeletedAndAddedEncoder
+ |> TsEncode.variantTagged "tasksUpdated" tasksUpdatedEncoder
+ |> TsEncode.buildUnion
+
+
+
+-- HELPERS
+
+
+renamedFileDecoder : TsDecode.Decoder ( String, String )
+renamedFileDecoder =
+ TsDecode.succeed Tuple.pair
+ |> TsDecode.andMap (TsDecode.field "oldPath" TsDecode.string)
+ |> TsDecode.andMap (TsDecode.field "newPath" TsDecode.string)
+
+
+tasksDeletedAndAddedEncoder : TsEncode.Encoder ( List String, List TaskItem )
+tasksDeletedAndAddedEncoder =
+ TsEncode.tuple (TsEncode.list TsEncode.string) (TsEncode.list TaskItem.encoder)
+
+
+tasksDeletedEncoder : TsEncode.Encoder (List TaskItem)
+tasksDeletedEncoder =
+ TsEncode.list <| TsEncode.map TaskItem.id TsEncode.string
+
+
+tasksUpdatedEncoder : TsEncode.Encoder (List ( String, TaskItem ))
+tasksUpdatedEncoder =
+ TsEncode.list <| TsEncode.tuple TsEncode.string TaskItem.encoder
diff --git a/src/Worker/InteropPorts.elm b/src/Worker/InteropPorts.elm
new file mode 100644
index 00000000..fcd815f5
--- /dev/null
+++ b/src/Worker/InteropPorts.elm
@@ -0,0 +1,97 @@
+port module Worker.InteropPorts exposing
+ ( allTaskItems
+ , allTasksLoaded
+ , decodeFlags
+ , tasksAdded
+ , tasksDeleted
+ , tasksDeletedAndAdded
+ , tasksUpdated
+ , toElm
+ )
+
+import Json.Decode
+import Json.Encode
+import TaskItem exposing (TaskItem)
+import TaskList exposing (TaskList)
+import TsJson.Decode as TsDecode
+import TsJson.Encode as TsEncode
+import Worker.InteropDefinitions as InteropDefinitions
+
+
+toElm : Sub (Result Json.Decode.Error InteropDefinitions.ToElm)
+toElm =
+ (InteropDefinitions.interop.toElm |> TsDecode.decoder)
+ |> Json.Decode.decodeValue
+ |> interopToElm
+
+
+
+-- FLAGS
+
+
+decodeFlags : Json.Decode.Value -> Result Json.Decode.Error InteropDefinitions.Flags
+decodeFlags flags =
+ Json.Decode.decodeValue
+ (InteropDefinitions.interop.flags |> TsDecode.decoder)
+ flags
+
+
+
+-- COMMANDS
+
+
+allTaskItems : TaskList -> Cmd msg
+allTaskItems taskList =
+ taskList
+ |> InteropDefinitions.AllTaskItems
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
+ |> interopFromElm
+
+
+allTasksLoaded : Cmd msg
+allTasksLoaded =
+ InteropDefinitions.AllTasksLoaded
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
+ |> interopFromElm
+
+
+tasksAdded : TaskList -> Cmd msg
+tasksAdded taskList =
+ taskList
+ |> InteropDefinitions.TasksAdded
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
+ |> interopFromElm
+
+
+tasksDeleted : List TaskItem -> Cmd msg
+tasksDeleted taskItems =
+ taskItems
+ |> InteropDefinitions.TasksDeleted
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
+ |> interopFromElm
+
+
+tasksDeletedAndAdded : List String -> List TaskItem -> Cmd msg
+tasksDeletedAndAdded toDelete toAdd =
+ ( toDelete, toAdd )
+ |> InteropDefinitions.TasksDeletedAndAdded
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
+ |> interopFromElm
+
+
+tasksUpdated : List ( String, TaskItem ) -> Cmd msg
+tasksUpdated updateDetails =
+ updateDetails
+ |> InteropDefinitions.TasksUpdated
+ |> TsEncode.encoder InteropDefinitions.interop.fromElm
+ |> interopFromElm
+
+
+
+-- PORTS
+
+
+port interopFromElm : Json.Encode.Value -> Cmd msg
+
+
+port interopToElm : (Json.Decode.Value -> msg) -> Sub msg
diff --git a/src/Worker/Session.elm b/src/Worker/Session.elm
new file mode 100644
index 00000000..c0d66d1b
--- /dev/null
+++ b/src/Worker/Session.elm
@@ -0,0 +1,138 @@
+module Worker.Session exposing
+ ( Session
+ , addTaskList
+ , dataviewTaskCompletion
+ , default
+ , finishAdding
+ , fromFlags
+ , removeTaskItems
+ , replaceTaskList
+ , taskList
+ )
+
+import DataviewTaskCompletion exposing (DataviewTaskCompletion)
+import State exposing (State)
+import TaskItem
+import TaskList exposing (TaskList)
+import Worker.InteropDefinitions as InteropDefinitions
+
+
+
+-- TYPES
+
+
+type Session
+ = Session Model
+
+
+type alias Model =
+ { dataviewTaskCompletion : DataviewTaskCompletion
+ , taskList : State TaskList
+ }
+
+
+
+-- CREATE
+
+
+default : Session
+default =
+ Session
+ { dataviewTaskCompletion = DataviewTaskCompletion.default
+ , taskList = State.Waiting
+ }
+
+
+fromFlags : InteropDefinitions.Flags -> Session
+fromFlags flags =
+ Session
+ { dataviewTaskCompletion = flags.dataviewTaskCompletion
+ , taskList = State.Waiting
+ }
+
+
+
+-- INFO
+
+
+dataviewTaskCompletion : Session -> DataviewTaskCompletion
+dataviewTaskCompletion (Session model) =
+ model.dataviewTaskCompletion
+
+
+taskList : Session -> TaskList
+taskList (Session model) =
+ case model.taskList of
+ State.Waiting ->
+ TaskList.empty
+
+ State.Loading currentList ->
+ currentList
+
+ State.Loaded currentList ->
+ currentList
+
+
+
+-- TASKLIST MANIPULATION
+
+
+addTaskList : TaskList -> Session -> Session
+addTaskList list ((Session model) as session) =
+ case model.taskList of
+ State.Waiting ->
+ updateTaskListState (State.Loading list) session
+
+ State.Loading currentList ->
+ updateTaskListState (State.Loading (TaskList.append currentList list)) session
+
+ State.Loaded currentList ->
+ updateTaskListState (State.Loaded (TaskList.append currentList list)) session
+
+
+finishAdding : Session -> Session
+finishAdding ((Session config) as session) =
+ case config.taskList of
+ State.Waiting ->
+ updateTaskListState (State.Loaded TaskList.empty) session
+
+ State.Loading list ->
+ updateTaskListState (State.Loaded list) session
+
+ State.Loaded _ ->
+ session
+
+
+removeTaskItems : List String -> Session -> Session
+removeTaskItems taskIds ((Session config) as session) =
+ case config.taskList of
+ State.Waiting ->
+ session
+
+ State.Loading currentList ->
+ updateTaskListState (State.Loading (TaskList.filter (\i -> not <| List.member (TaskItem.id i) taskIds) currentList)) session
+
+ State.Loaded currentList ->
+ updateTaskListState (State.Loaded (TaskList.filter (\i -> not <| List.member (TaskItem.id i) taskIds) currentList)) session
+
+
+replaceTaskList : TaskList -> Session -> Session
+replaceTaskList newList ((Session config) as session) =
+ case config.taskList of
+ State.Waiting ->
+ session
+
+ State.Loading _ ->
+ updateTaskListState (State.Loading newList) session
+
+ State.Loaded _ ->
+ updateTaskListState (State.Loaded newList) session
+
+
+
+-- PRIVATE
+
+
+updateTaskListState : State TaskList -> Session -> Session
+updateTaskListState taskListState (Session model) =
+ Session { model | taskList = taskListState }
diff --git a/tests/DecodeHelpersTests.elm b/tests/DecodeHelpersTests.elm
new file mode 100644
index 00000000..300f5db0
--- /dev/null
+++ b/tests/DecodeHelpersTests.elm
@@ -0,0 +1,26 @@
+module DecodeHelpersTests exposing (suite)
+
+import Date
+import DecodeHelpers
+import Expect
+import Helpers.DecodeHelpers as DecodeTestHelpers
+import Test exposing (..)
+
+
+suite : Test
+suite =
+ concat
+ [ decoder
+ ]
+
+
+decoder : Test
+decoder =
+ describe "decoder"
+ [ test "decodes a RataDie integer as a date" <|
+ \() ->
+ "123456"
+ |> DecodeTestHelpers.runDecoder DecodeHelpers.dateDecoder
+ |> .decoded
+ |> Expect.equal (Ok <| Date.fromRataDie 123456)
+ ]
diff --git a/tests/DueDateTests.elm b/tests/DueDateTests.elm
new file mode 100644
index 00000000..a2ac88d3
--- /dev/null
+++ b/tests/DueDateTests.elm
@@ -0,0 +1,64 @@
+module DueDateTests exposing (suite)
+
+import Date
+import DueDate
+import Expect
+import Helpers.DecodeHelpers as DecodeTestHelpers
+import Test exposing (..)
+import TsJson.Encode as TsEncode
+
+
+suite : Test
+suite =
+ concat
+ [ decoder
+ , encoder
+ ]
+
+
+decoder : Test
+decoder =
+ describe "decoder"
+ [ test "decodes a NotSet" <|
+ \() ->
+ """{"tag":"NotSet"}"""
+ |> DecodeTestHelpers.runDecoder DueDate.decoder
+ |> .decoded
+ |> Expect.equal (Ok DueDate.NotSet)
+ , test "decodes a SetToDate" <|
+ \() ->
+ """{"tag":"SetToDate","date":123456}"""
+ |> DecodeTestHelpers.runDecoder DueDate.decoder
+ |> .decoded
+ |> Expect.equal (Ok <| DueDate.SetToDate (Date.fromRataDie 123456))
+ , test "decodes a SetToNone" <|
+ \() ->
+ """{"tag":"SetToNone"}"""
+ |> DecodeTestHelpers.runDecoder DueDate.decoder
+ |> .decoded
+ |> Expect.equal (Ok DueDate.SetToNone)
+ ]
+
+
+encoder : Test
+encoder =
+ describe "encoder"
+ [ test "encodes a NotSet" <|
+ \() ->
+ DueDate.NotSet
+ |> TsEncode.runExample DueDate.encoder
+ |> .output
+ |> Expect.equal "{\"tag\":\"NotSet\"}"
+ , test "encodes a SetToDate" <|
+ \() ->
+ DueDate.SetToDate (Date.fromRataDie 123123)
+ |> TsEncode.runExample DueDate.encoder
+ |> .output
+ |> Expect.equal "{\"tag\":\"SetToDate\",\"date\":123123}"
+ , test "encodes a SetToNone" <|
+ \() ->
+ DueDate.SetToNone
+ |> TsEncode.runExample DueDate.encoder
+ |> .output
+ |> Expect.equal "{\"tag\":\"SetToNone\"}"
+ ]
diff --git a/tests/EncodeHelpersTests.elm b/tests/EncodeHelpersTests.elm
new file mode 100644
index 00000000..2d767372
--- /dev/null
+++ b/tests/EncodeHelpersTests.elm
@@ -0,0 +1,26 @@
+module EncodeHelpersTests exposing (suite)
+
+import Date
+import EncodeHelpers
+import Expect
+import Test exposing (..)
+import TsJson.Encode as TsEncode
+
+
+suite : Test
+suite =
+ concat
+ [ encoder
+ ]
+
+
+encoder : Test
+encoder =
+ describe "encoder"
+ [ test "encodes the date as a RataDie integer" <|
+ \() ->
+ Date.fromRataDie 123456
+ |> TsEncode.runExample EncodeHelpers.dateEncoder
+ |> .output
+ |> Expect.equal "123456"
+ ]
diff --git a/tests/Helpers/TaskItemHelpers.elm b/tests/Helpers/TaskItemHelpers.elm
index e24cd64f..ac05715c 100644
--- a/tests/Helpers/TaskItemHelpers.elm
+++ b/tests/Helpers/TaskItemHelpers.elm
@@ -1,6 +1,7 @@
module Helpers.TaskItemHelpers exposing
( basicParser
, exampleTaskItem
+ , safeTaskItem
)
import DataviewTaskCompletion
@@ -18,3 +19,9 @@ exampleTaskItem : String -> String -> TaskItem
exampleTaskItem markdown path =
Parser.run (TaskItem.parser DataviewTaskCompletion.NoCompletion path Nothing TagList.empty 0) markdown
|> Result.withDefault TaskItem.dummy
+
+
+safeTaskItem : String -> TaskItem
+safeTaskItem markdown =
+ Parser.run basicParser markdown
+ |> Result.withDefault TaskItem.dummy
diff --git a/tests/Helpers/TaskListHelpers.elm b/tests/Helpers/TaskListHelpers.elm
index 5f138d28..51de28a5 100644
--- a/tests/Helpers/TaskListHelpers.elm
+++ b/tests/Helpers/TaskListHelpers.elm
@@ -1,6 +1,5 @@
module Helpers.TaskListHelpers exposing
- ( basicParser
- , exampleDateBoardTaskList
+ ( exampleDateBoardTaskList
, exampleTagBoardTaskList
, parsedTasks
, taskListFromFile
@@ -11,20 +10,29 @@ module Helpers.TaskListHelpers exposing
import DataviewTaskCompletion
import Helpers.DateTimeHelpers as DateTimeHelpers
-import Parser exposing (Parser)
+import MarkdownFile exposing (MarkdownFile)
import TagList
import TaskList exposing (TaskList)
-basicParser : Parser TaskList
-basicParser =
- TaskList.parser DataviewTaskCompletion.NoCompletion "" Nothing TagList.empty 0
+exampleDateBoardTaskList : TaskList
+exampleDateBoardTaskList =
+ exampleDateBoardTasks
+ |> List.map parsedTasks
+ |> TaskList.concat
+
+
+exampleTagBoardTaskList : TaskList
+exampleTagBoardTaskList =
+ exampleTagBoardTasks
+ |> List.map parsedTasks
+ |> TaskList.concat
parsedTasks : ( String, Maybe String, String ) -> TaskList
parsedTasks ( p, d, ts ) =
- Parser.run (TaskList.parser DataviewTaskCompletion.NoCompletion p d TagList.empty 0) ts
- |> Result.withDefault TaskList.empty
+ basicMarkdown p d ts
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
taskListFromFile : String -> TaskList
@@ -51,20 +59,6 @@ taskListFromNewFile path =
|> parsedTasks
-exampleDateBoardTaskList : TaskList
-exampleDateBoardTaskList =
- exampleDateBoardTasks
- |> List.map parsedTasks
- |> TaskList.concat
-
-
-exampleTagBoardTaskList : TaskList
-exampleTagBoardTaskList =
- exampleTagBoardTasks
- |> List.map parsedTasks
- |> TaskList.concat
-
-
-- HELPERS
@@ -189,3 +183,17 @@ exampleTagBoardTasks =
- [x] c.tag4 #tag4
""" )
]
+
+
+
+-- HELPERS
+
+
+basicMarkdown : String -> Maybe String -> String -> MarkdownFile
+basicMarkdown path date body =
+ { filePath = path
+ , fileDate = date
+ , frontMatterTags = TagList.empty
+ , bodyOffset = 0
+ , body = body
+ }
diff --git a/tests/InteropDefinitionsTests.elm b/tests/InteropDefinitionsTests.elm
index b0971dcd..6e32ac6b 100644
--- a/tests/InteropDefinitionsTests.elm
+++ b/tests/InteropDefinitionsTests.elm
@@ -14,10 +14,13 @@ import Filter
import GlobalSettings
import Helpers.DecodeHelpers as DecodeHelpers
import Helpers.FilterHelpers as FilterHelpers
+import Helpers.TaskItemHelpers as TaskItemHelpers
import InteropDefinitions exposing (interop)
+import Parser
import SafeZipper
import Semver
-import TagList
+import TaskItem exposing (TaskItem, TaskItemFields)
+import TaskList exposing (TaskList)
import Test exposing (..)
import TextDirection
import Time
@@ -994,11 +997,11 @@ fromElmTests =
|> Expect.equal """{"tag":"closeView"}"""
, test "encodes DeleteTask data" <|
\() ->
- { filePath = "a path", lineNumber = 33, originalText = "the text" }
+ { defaultTaskItemFields | filePath = "a path", lineNumber = 33, originalLine = "the text" }
|> InteropDefinitions.DeleteTask
|> TsEncode.runExample interop.fromElm
|> .output
- |> Expect.equal """{"tag":"deleteTask","data":{"filePath":"a path","lineNumber":33,"originalText":"the text"}}"""
+ |> Expect.equal """{"tag":"deleteTask","data":{"filePath":"a path","lineNumber":33,"originalLine":"the text"}}"""
, test "encodes DisplayTaskMarkdown data" <|
\() ->
[ { filePath = "a path", taskMarkdown = [ { id = "an id", markdown = "some markdown" } ] } ]
@@ -1008,17 +1011,18 @@ fromElmTests =
|> Expect.equal """{"tag":"displayTaskMarkdown","data":[{"filePath":"a path","taskMarkdown":[{"id":"an id","markdown":"some markdown"}]}]}"""
, test "encodes ElmInitialized" <|
\() ->
- InteropDefinitions.ElmInitialized
+ "a_unique_id"
+ |> InteropDefinitions.ElmInitialized
|> TsEncode.runExample interop.fromElm
|> .output
- |> Expect.equal """{"tag":"elmInitialized"}"""
+ |> Expect.equal """{"tag":"elmInitialized","data":"a_unique_id"}"""
, test "encodes OpenTaskSourceFile data" <|
\() ->
- { filePath = "a path", lineNumber = 33, originalText = "the text" }
+ { defaultTaskItemFields | filePath = "a path", lineNumber = 33, originalLine = "the text" }
|> InteropDefinitions.OpenTaskSourceFile
|> TsEncode.runExample interop.fromElm
|> .output
- |> Expect.equal """{"tag":"openTaskSourceFile","data":{"filePath":"a path","lineNumber":33,"originalText":"the text"}}"""
+ |> Expect.equal """{"tag":"openTaskSourceFile","data":{"filePath":"a path","lineNumber":33,"originalLine":"the text"}}"""
, test "encodes RequestFilterCandidates" <|
\() ->
InteropDefinitions.RequestFilterCandidates
@@ -1041,11 +1045,11 @@ fromElmTests =
|> Expect.equal """{"tag":"trackDraggable","data":{"dragType":"someDragType","clientPos":{"x":1.1,"y":2.2},"draggableId":"id of draggable"}}"""
, test "encodes UpdateTasks data" <|
\() ->
- { filePath = "a path", tasks = [ { lineNumber = 12, originalText = "what was there", newText = "new text" } ] }
+ { filePath = "a path", tasks = [ { lineNumber = 12, originalLine = "what was there", newText = "new text" } ] }
|> InteropDefinitions.UpdateTasks
|> TsEncode.runExample interop.fromElm
|> .output
- |> Expect.equal """{"tag":"updateTasks","data":{"filePath":"a path","tasks":[{"lineNumber":12,"originalText":"what was there","newText":"new text"}]}}"""
+ |> Expect.equal """{"tag":"updateTasks","data":{"filePath":"a path","tasks":[{"lineNumber":12,"originalLine":"what was there","newText":"new text"}]}}"""
]
@@ -1058,12 +1062,6 @@ toElmTests =
|> DecodeHelpers.runDecoder interop.toElm
|> .decoded
|> Expect.equal (Ok <| InteropDefinitions.ActiveStateUpdated False)
- , test "decodes allMarkdownLoaded" <|
- \() ->
- """{"tag":"allMarkdownLoaded","data":{}}"""
- |> DecodeHelpers.runDecoder interop.toElm
- |> .decoded
- |> Expect.equal (Ok <| InteropDefinitions.AllMarkdownLoaded)
, test "decodes configChanged data" <|
\() ->
"""{"tag":"configChanged","data":{"rightToLeft":true}}"""
@@ -1090,54 +1088,12 @@ toElmTests =
]
}
)
- , test "decodes fileAdded data" <|
- \() ->
- """{"tag":"fileAdded","data":{"filePath":"a path","fileDate":"a date","fileContents":"---\\ntags: [ a_tag ]\\n---\\nsome contents"}}"""
- |> DecodeHelpers.runDecoder interop.toElm
- |> .decoded
- |> Expect.equal
- (Ok <|
- InteropDefinitions.FileAdded
- { filePath = "a path"
- , fileDate = Just "a date"
- , frontMatterTags = TagList.fromList [ "a_tag" ]
- , bodyOffset = 3
- , body = "some contents"
- }
- )
- , test "decodes fileDeleted data" <|
- \() ->
- """{"tag":"fileDeleted","data":"a path"}"""
- |> DecodeHelpers.runDecoder interop.toElm
- |> .decoded
- |> Expect.equal (Ok <| InteropDefinitions.FileDeleted "a path")
, test "decodes editCardDueDate data" <|
\() ->
"""{"tag":"editCardDueDate","data":"a card"}"""
|> DecodeHelpers.runDecoder interop.toElm
|> .decoded
|> Expect.equal (Ok <| InteropDefinitions.EditCardDueDate "a card")
- , test "decodes fileRenamed data" <|
- \() ->
- """{"tag":"fileRenamed","data":{"oldPath":"the old path","newPath":"the new path"}}"""
- |> DecodeHelpers.runDecoder interop.toElm
- |> .decoded
- |> Expect.equal (Ok <| InteropDefinitions.FileRenamed ( "the old path", "the new path" ))
- , test "decodes fileUpdated data" <|
- \() ->
- """{"tag":"fileUpdated","data":{"filePath":"a path","fileDate":"a date","frontMatterTags":["a_tag"],"fileContents":"---\\ntags: [ a_tag ]\\n---\\nsome contents"}}"""
- |> DecodeHelpers.runDecoder interop.toElm
- |> .decoded
- |> Expect.equal
- (Ok <|
- InteropDefinitions.FileUpdated
- { filePath = "a path"
- , fileDate = Just "a date"
- , frontMatterTags = TagList.fromList [ "a_tag" ]
- , bodyOffset = 3
- , body = "some contents"
- }
- )
, test "decodes filterCandidates data" <|
\() ->
"""{"tag":"filterCandidates","data":[{"tag":"pathFilter","data":"a path"},{"tag":"pathFilter","data":"another path"}]}"""
@@ -1212,4 +1168,68 @@ toElmTests =
|> .decoded
|> Result.toMaybe
|> Expect.equal Nothing
+ , test "decodes taskItemsAdded data" <|
+ \() ->
+ let
+ taskList : TaskList
+ taskList =
+ TaskList.empty
+ |> TaskList.add (taskItem "- [ ] foo")
+ in
+ """{"tag":"taskItemsAdded","data":[{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[]}]}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal (Ok <| InteropDefinitions.TaskItemsAdded taskList)
+ , test "decodes taskItemsDeleted data" <|
+ \() ->
+ """{"tag":"taskItemsDeleted","data":["foo","bar","baz"]}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal (Ok <| InteropDefinitions.TaskItemsDeleted [ "foo", "bar", "baz" ])
+ , test "decodes taskItemsDeletedAndAdded data" <|
+ \() ->
+ let
+ toAdd : List TaskItem
+ toAdd =
+ TaskList.empty
+ |> TaskList.add (taskItem "- [ ] bar")
+ |> TaskList.toList
+ in
+ """{"tag":"taskItemsDeletedAndAdded","data":[["taskId:1"],[{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"bar"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] bar","originalLine":"- [ ] bar","tags":[],"title":["bar"]},"subFields":[]}]]}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal (Ok <| InteropDefinitions.TaskItemsDeletedAndAdded ( [ "taskId:1" ], toAdd ))
+ , test "decodes taskItemsRefreshed data" <|
+ \() ->
+ let
+ taskList : TaskList
+ taskList =
+ TaskList.empty
+ |> TaskList.add (taskItem "- [ ] foo")
+ in
+ """{"tag":"taskItemsRefreshed","data":[{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[]}]}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal (Ok <| InteropDefinitions.TaskItemsRefreshed taskList)
+ , test "decodes taskItemsUpdated data" <|
+ \() ->
+ """{"tag":"taskItemsUpdated","data":[["bar",{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[]}]]}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal (Ok <| InteropDefinitions.TaskItemsUpdated [ ( "bar", taskItem "- [ ] foo" ) ])
]
+
+
+
+-- HELPERS
+
+
+defaultTaskItemFields : TaskItemFields
+defaultTaskItemFields =
+ TaskItem.fields TaskItem.dummy
+
+
+taskItem : String -> TaskItem
+taskItem markdown =
+ Parser.run TaskItemHelpers.basicParser markdown
+ |> Result.withDefault TaskItem.dummy
diff --git a/tests/SessionTests.elm b/tests/SessionTests.elm
index c14545c9..9f50aee6 100644
--- a/tests/SessionTests.elm
+++ b/tests/SessionTests.elm
@@ -12,12 +12,14 @@ import Expect
import Filter
import GlobalSettings exposing (GlobalSettings)
import Helpers.FilterHelpers as FilterHelpers
+import Helpers.TaskItemHelpers as TaskItemHelpers
import Helpers.TaskListHelpers as TaskListHelpers
import InteropDefinitions exposing (Flags)
+import Parser
import SafeZipper
import Session
import Settings exposing (Settings)
-import TaskItem
+import TaskItem exposing (TaskItem)
import TaskList
import Test exposing (..)
import Time
@@ -29,14 +31,15 @@ suite =
[ addTaskList
, cards
, default
- , deleteItemsFromFile
, findCard
, finishAdding
, firstDayOfWeek
, fromFlags
, globalSettings
, moveDragable
+ , removeTaskItems
, replaceTaskItems
+ , replaceTaskList
, stopTrackingDragable
, updatePath
, waitForDrag
@@ -141,38 +144,6 @@ default =
]
-deleteItemsFromFile : Test
-deleteItemsFromFile =
- describe "deleteItemsFromFile"
- [ test "does nothing to the tasklist of a new session" <|
- \() ->
- Session.default
- |> Session.deleteItemsFromFile ""
- |> Session.taskList
- |> TaskList.taskTitles
- |> Expect.equal []
- , test "remove tasks from the given file during loading" <|
- \() ->
- Session.default
- |> Session.addTaskList TaskListHelpers.taskListFromFileA
- |> Session.addTaskList TaskListHelpers.taskListFromFileG
- |> Session.deleteItemsFromFile "g"
- |> Session.taskList
- |> TaskList.taskTitles
- |> Expect.equal [ "a1", "a2" ]
- , test "remove tasks from the given file after loading has finished" <|
- \() ->
- Session.default
- |> Session.addTaskList TaskListHelpers.taskListFromFileA
- |> Session.addTaskList TaskListHelpers.taskListFromFileG
- |> Session.finishAdding
- |> Session.deleteItemsFromFile "g"
- |> Session.taskList
- |> TaskList.taskTitles
- |> Expect.equal [ "a1", "a2" ]
- ]
-
-
findCard : Test
findCard =
describe "findCard"
@@ -351,13 +322,47 @@ moveDragable =
]
+removeTaskItems : Test
+removeTaskItems =
+ describe "removeTaskItems"
+ [ test "does nothing if there are no taskItems in the Session" <|
+ \() ->
+ Session.default
+ |> Session.removeTaskItems []
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal []
+ , test "removes tasks with the given ids whilst loading tasklists" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.addTaskList TaskListHelpers.taskListFromFileG
+ |> Session.removeTaskItems [ "3826002220:2", "3792446982:3" ]
+ |> Session.taskList
+ |> TaskList.toList
+ |> List.map TaskItem.id
+ |> Expect.equal [ "3826002220:3", "3792446982:2" ]
+ , test "removes tasks with the given ids if finished loading tasklists" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.addTaskList TaskListHelpers.taskListFromFileG
+ |> Session.finishAdding
+ |> Session.removeTaskItems [ "3826002220:2", "3792446982:3" ]
+ |> Session.taskList
+ |> TaskList.toList
+ |> List.map TaskItem.id
+ |> Expect.equal [ "3826002220:3", "3792446982:2" ]
+ ]
+
+
replaceTaskItems : Test
replaceTaskItems =
describe "replaceTaskItems"
[ test "does nothing on a new session" <|
\() ->
Session.default
- |> Session.replaceTaskItems "" TaskListHelpers.taskListFromFileA
+ |> Session.replaceTaskItems []
|> Session.taskList
|> TaskList.taskTitles
|> Expect.equal []
@@ -366,20 +371,54 @@ replaceTaskItems =
Session.default
|> Session.addTaskList TaskListHelpers.taskListFromFileA
|> Session.addTaskList TaskListHelpers.taskListFromFileG
- |> Session.replaceTaskItems "a" (TaskListHelpers.taskListFromNewFile "path")
+ |> Session.replaceTaskItems [ ( "3826002220:3", taskItem "- [ ] foo" ) ]
|> Session.taskList
|> TaskList.taskTitles
- |> Expect.equal [ "n1", "n2", "g1", "g2" ]
- , test "replaces tasks from the file with those given whilst loading tasklists even if finished adding" <|
+ |> List.sort
+ |> Expect.equal (List.sort [ "a1", "foo", "g1", "g2" ])
+ , test "replaces tasks from the file with those given when finished loading tasklists" <|
\() ->
Session.default
|> Session.addTaskList TaskListHelpers.taskListFromFileA
|> Session.addTaskList TaskListHelpers.taskListFromFileG
|> Session.finishAdding
- |> Session.replaceTaskItems "g" (TaskListHelpers.taskListFromNewFile "path")
+ |> Session.replaceTaskItems [ ( "3826002220:3", taskItem "- [ ] foo" ) ]
|> Session.taskList
|> TaskList.taskTitles
- |> Expect.equal [ "n1", "n2", "a1", "a2" ]
+ |> List.sort
+ |> Expect.equal (List.sort [ "a1", "foo", "g1", "g2" ])
+ ]
+
+
+replaceTaskList : Test
+replaceTaskList =
+ describe "replaceTaskList"
+ [ test "replaces all tasks with those given if a new session" <|
+ \() ->
+ Session.default
+ |> Session.replaceTaskList TaskListHelpers.taskListFromFileA
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "a1", "a2" ]
+ , test "replaces all tasks with those given whilst loading tasklists" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.addTaskList TaskListHelpers.taskListFromFileG
+ |> Session.replaceTaskList (TaskListHelpers.taskListFromNewFile "path")
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "n1", "n2" ]
+ , test "replaces all tasks with those given whilst loading tasklists even if finished adding" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.addTaskList TaskListHelpers.taskListFromFileG
+ |> Session.finishAdding
+ |> Session.replaceTaskList (TaskListHelpers.taskListFromNewFile "path")
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "n1", "n2" ]
]
@@ -444,6 +483,11 @@ updatePath =
-- HELPERS
+defaultGlobalSettings : GlobalSettings
+defaultGlobalSettings =
+ GlobalSettings.default
+
+
exampleFlags : Flags
exampleFlags =
{ dataviewTaskCompletion = DataviewTaskCompletion.NoCompletion
@@ -481,9 +525,10 @@ exampleSettings =
)
-defaultGlobalSettings : GlobalSettings
-defaultGlobalSettings =
- GlobalSettings.default
+taskItem : String -> TaskItem
+taskItem markdown =
+ Parser.run TaskItemHelpers.basicParser markdown
+ |> Result.withDefault TaskItem.dummy
untaggedAndCompleted : Settings
diff --git a/tests/TagListTests.elm b/tests/TagListTests.elm
index 3cc5e397..99ae3cdb 100644
--- a/tests/TagListTests.elm
+++ b/tests/TagListTests.elm
@@ -1,10 +1,12 @@
module TagListTests exposing (suite)
import Expect
+import Helpers.DecodeHelpers as DecodeTestHelpers
import Parser
import Tag exposing (Tag)
import TagList exposing (TagList)
import Test exposing (..)
+import TsJson.Encode as TsEncode
suite : Test
@@ -15,7 +17,9 @@ suite =
, containsTagMatching
, containsTagMatchingOneOf
, containsTagOtherThanThese
+ , decoder
, empty
+ , encoder
, filter
, fromList
, isEmpty
@@ -197,6 +201,18 @@ containsTagOtherThanThese =
]
+decoder : Test
+decoder =
+ describe "decoder"
+ [ test "decodes from a list of strings" <|
+ \() ->
+ "[\"atag\",\"btag\"]"
+ |> DecodeTestHelpers.runDecoder TagList.decoder
+ |> .decoded
+ |> Expect.equal (Ok <| TagList.fromList [ "atag", "btag" ])
+ ]
+
+
empty : Test
empty =
describe "empty"
@@ -208,6 +224,18 @@ empty =
]
+encoder : Test
+encoder =
+ describe "encoder"
+ [ test "encodes the list as a list of strings" <|
+ \() ->
+ TagList.fromList [ "atag", "btag" ]
+ |> TsEncode.runExample TagList.encoder
+ |> .output
+ |> Expect.equal "[\"atag\",\"btag\"]"
+ ]
+
+
filter : Test
filter =
describe "filter"
diff --git a/tests/TagTests.elm b/tests/TagTests.elm
index 15a94508..2f630449 100644
--- a/tests/TagTests.elm
+++ b/tests/TagTests.elm
@@ -2,10 +2,12 @@ module TagTests exposing (suite)
import Expect
import Fuzz exposing (Fuzzer)
+import Helpers.DecodeHelpers as DecodeTestHelpers
import Helpers.UnicodeHelpers as UnicodeHelpers
import Parser exposing ((|.), (|=))
import Tag
import Test exposing (..)
+import TsJson.Encode as TsEncode
import Unicode
@@ -13,6 +15,8 @@ suite : Test
suite =
concat
[ containsInvalidCharacters
+ , decoder
+ , encoder
, equals
, matches
, parser
@@ -47,6 +51,32 @@ containsInvalidCharacters =
]
+decoder : Test
+decoder =
+ describe "decoder"
+ [ test "decodes a tag from a string" <|
+ \() ->
+ "\"foo\""
+ |> DecodeTestHelpers.runDecoder Tag.decoder
+ |> .decoded
+ |> Result.map Tag.toString
+ |> Expect.equal (Ok "foo")
+ ]
+
+
+encoder : Test
+encoder =
+ describe "encoder"
+ [ test "encodes the tag contents as a string" <|
+ \() ->
+ "#foo"
+ |> Parser.run Tag.parser
+ |> Result.map (TsEncode.runExample Tag.encoder)
+ |> Result.map .output
+ |> Expect.equal (Ok "\"foo\"")
+ ]
+
+
equals : Test
equals =
describe "equals"
diff --git a/tests/TaskItemTests.elm b/tests/TaskItemTests.elm
index d2a36658..feb0268c 100644
--- a/tests/TaskItemTests.elm
+++ b/tests/TaskItemTests.elm
@@ -4,14 +4,16 @@ import DataviewTaskCompletion
import Date
import Expect
import Filter
+import Helpers.DecodeHelpers as DecodeTestHelpers
import Helpers.FilterHelpers as FilterHelpers
import Helpers.TaskHelpers as TaskHelpers
import Helpers.TaskItemHelpers as TaskItemHelpers
import Parser exposing ((|=))
import TagList
-import TaskItem exposing (Completion(..))
+import TaskItem exposing (Completion(..), TaskItem)
import Test exposing (..)
import Time
+import TsJson.Encode as TsEncode
suite : Test
@@ -19,11 +21,14 @@ suite =
concat
[ allSubtasksWithMatchingTagCompleted
, blockLink
+ , compare
, completedPosix
, completion
, containsId
+ , decoder
, descendantTasks
, due
+ , encoder
, filePath
, hasTags
, hasThisTagBasic
@@ -37,7 +42,8 @@ suite =
, isDated
, lineNumber
, notes
- , originalText
+ , originalBlock
+ , originalLine
, parsing
, removeMatchingTags
, removeTags
@@ -123,6 +129,212 @@ blockLink =
]
+compare : Test
+compare =
+ describe "compare"
+ [ test "returns Identical for two basic taskItems" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 0 "- [ ] foo"
+ in
+ taskItemPlus "a" 0 "- [ ] foo"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Identical
+ , test "returns Identical for two taskItems with notes" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 0 "- [ ] foo\n some notes\n"
+ in
+ taskItemPlus "a" 0 "- [ ] foo\n some notes\n"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Identical
+ , test "returns Identical for two taskItems with notes and subtasks" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 0 "- [ ] foo\n some notes\n - [ ] bar\n"
+ in
+ taskItemPlus "a" 0 "- [ ] foo\n some notes\n - [ ] bar\n"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Identical
+ , test "returns Updated if a subtask has been added" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 0 "- [ ] foo\n - [ ] bar\n"
+ in
+ taskItemPlus "a" 0 "- [ ] foo"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Updated
+ , test "returns Updated if a subtask and notes have been added" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 0 "- [ ] foo\n some notes\n - [ ] bar\n"
+ in
+ taskItemPlus "a" 0 "- [ ] foo"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Updated
+ , test "returns Updated if the subtask and notes have been edited" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 0 "- [ ] foo\n some edited notes\n - [ ] bar-foo\n"
+ in
+ taskItemPlus "a" 0 "- [ ] foo\n some notes\n - [ ] bar\n"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Updated
+ , test "returns Updated if the subtask and notes have been deleted" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 0 "- [ ] foo"
+ in
+ taskItemPlus "a" 0 "- [ ] foo\n some edited notes\n - [ ] bar-foo\n"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Updated
+ , test "returns Updated if tags have been added" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 0 "- [ ] foo #bar @due(2021-01-01)"
+ in
+ taskItemPlus "a" 0 "- [ ] foo"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Updated
+ , test "returns Updated if the title 'is similar'" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 0 "- [ ] this is something I need to do"
+ in
+ taskItemPlus "a" 0 "- [ ] this is something"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Updated
+ , test "returns Moved if a basic task block is the same but it is in a different location" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 1 "- [ ] foo"
+ in
+ taskItemPlus "a" 0 "- [ ] foo"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Moved
+ , test "returns Moved if a task block with notes and subtasks is the same but it is in a different location" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 1 "- [ ] foo\n some notes\n - [ ] bar\n"
+ in
+ taskItemPlus "a" 0 "- [ ] foo\n some notes\n - [ ] bar\n"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Moved
+ , test "returns Moved if a task block with notes is the same but it is in a different location" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 1 "- [ ] foo\n some notes\n"
+ in
+ taskItemPlus "a" 0 "- [ ] foo\n some notes\n"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Moved
+ , test "returns MovedAndUpdated if a subtask has been added and it has been moved" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 1 "- [ ] foo\n - [ ] bar\n"
+ in
+ taskItemPlus "a" 0 "- [ ] foo"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.MovedAndUpdated
+ , test "returns MovedAndUpdated if a subtask and notes have been added and it has been moved" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 1 "- [ ] foo\n some notes\n - [ ] bar\n"
+ in
+ taskItemPlus "a" 0 "- [ ] foo"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.MovedAndUpdated
+ , test "returns MovedAndUpdated if the subtask and notes have been edited and it has been moved" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 1 "- [ ] foo\n some edited notes\n - [ ] bar-foo\n"
+ in
+ taskItemPlus "a" 0 "- [ ] foo\n some notes\n - [ ] bar\n"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.MovedAndUpdated
+ , test "returns MovedAndUpdated if the subtask and notes have been deleted and it has been moved" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 1 "- [ ] foo"
+ in
+ taskItemPlus "a" 0 "- [ ] foo\n some edited notes\n - [ ] bar-foo\n"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.MovedAndUpdated
+ , test "returns MovedAndUpdated if tags have been added and it has been moved" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 1 "- [ ] foo #bar @due(2021-01-01)"
+ in
+ taskItemPlus "a" 0 "- [ ] foo"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.MovedAndUpdated
+ , test "returns Updated if the title 'is similar' and it has been moved" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 1 "- [ ] this is something xxxx"
+ in
+ taskItemPlus "a" 0 "- [ ] this is something"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.MovedAndUpdated
+ , test "is more fussy about similarity when moved and edited than when just edited" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 1 "- [ ] this is something I need to do"
+ in
+ taskItemPlus "a" 0 "- [ ] this is something"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Different
+ , test "returns Different if the top level title is different" <|
+ \() ->
+ let
+ other : TaskItem
+ other =
+ taskItemPlus "a" 0 "- [ ] foo"
+ in
+ taskItemPlus "a" 0 "- [ ] bar"
+ |> TaskItem.compare other
+ |> Expect.equal TaskItem.Different
+ ]
+
+
completedPosix : Test
completedPosix =
describe "completedPosix"
@@ -315,6 +527,47 @@ containsId =
]
+decoder : Test
+decoder =
+ describe "decoder"
+ [ test "decodes a basic TaskItem" <|
+ \() ->
+ """{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[]}"""
+ |> DecodeTestHelpers.runDecoder TaskItem.decoder
+ |> .decoded
+ |> Result.map TaskItem.title
+ |> Expect.equal (Ok <| "foo")
+ , test "decodes a well decorated TaskItem" <|
+ \() ->
+ let
+ taskItem : TaskItem
+ taskItem =
+ "- [x] foo #bar @autocomplete(true) [due:: 2024-01-01]"
+ |> Parser.run (TaskItem.parser DataviewTaskCompletion.NoCompletion "" Nothing TagList.empty 0)
+ |> Result.withDefault TaskItem.dummy
+ in
+ """{"fields":{"autoComplete":{"tag":"TrueSpecified"},"completion":{"tag":"Completed"},"contents":[{"tag":"DueTag","data":{"tag":"SetToDate","date":738886}},{"tag":"AutoCompleteTag","data":{"tag":"TrueSpecified"}},{"tag":"ObsidianTag","data":"bar"},{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"SetToDate","date":738886},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [x] foo #bar @autocomplete(true) [due:: 2024-01-01]","originalLine":"- [x] foo #bar @autocomplete(true) [due:: 2024-01-01]","tags":["bar"],"title":["foo"]},"subFields":[]}"""
+ |> DecodeTestHelpers.runDecoder TaskItem.decoder
+ |> .decoded
+ |> Result.toMaybe
+ |> Expect.equal (Just taskItem)
+ , test "decodes a TaskItem with subtasks" <|
+ \() ->
+ let
+ taskItem : TaskItem
+ taskItem =
+ "- [ ] foo\n - [ ] bar"
+ |> Parser.run (TaskItem.parser DataviewTaskCompletion.NoCompletion "" Nothing TagList.empty 0)
+ |> Result.withDefault TaskItem.dummy
+ in
+ """{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo\\n - [ ] bar","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"bar"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":2,"notes":"","originalBlock":"","originalLine":" - [ ] bar","tags":[],"title":["bar"]}]}"""
+ |> DecodeTestHelpers.runDecoder TaskItem.decoder
+ |> .decoded
+ |> Result.toMaybe
+ |> Expect.equal (Just taskItem)
+ ]
+
+
descendantTasks : Test
descendantTasks =
describe "descendantTasks"
@@ -499,6 +752,33 @@ due =
]
+encoder : Test
+encoder =
+ describe "encoder"
+ [ test "encodes a basic TaskItem" <|
+ \() ->
+ "- [ ] foo"
+ |> Parser.run (TaskItem.parser DataviewTaskCompletion.NoCompletion "" Nothing TagList.empty 0)
+ |> Result.map (TsEncode.runExample TaskItem.encoder)
+ |> Result.map .output
+ |> Expect.equal (Ok """{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[]}""")
+ , test "encodes a well decorated TaskItem" <|
+ \() ->
+ "- [x] foo #bar @autocomplete(true) [due:: 2024-01-01]"
+ |> Parser.run (TaskItem.parser DataviewTaskCompletion.NoCompletion "" Nothing TagList.empty 0)
+ |> Result.map (TsEncode.runExample TaskItem.encoder)
+ |> Result.map .output
+ |> Expect.equal (Ok """{"fields":{"autoComplete":{"tag":"TrueSpecified"},"completion":{"tag":"Completed"},"contents":[{"tag":"DueTag","data":{"tag":"SetToDate","date":738886}},{"tag":"AutoCompleteTag","data":{"tag":"TrueSpecified"}},{"tag":"ObsidianTag","data":"bar"},{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"SetToDate","date":738886},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [x] foo #bar @autocomplete(true) [due:: 2024-01-01]","originalLine":"- [x] foo #bar @autocomplete(true) [due:: 2024-01-01]","tags":["bar"],"title":["foo"]},"subFields":[]}""")
+ , test "encodes a TaskItem with subtasks" <|
+ \() ->
+ "- [ ] foo\n - [ ] bar"
+ |> Parser.run (TaskItem.parser DataviewTaskCompletion.NoCompletion "" Nothing TagList.empty 0)
+ |> Result.map (TsEncode.runExample TaskItem.encoder)
+ |> Result.map .output
+ |> Expect.equal (Ok """{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo\\n - [ ] bar","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"bar"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":2,"notes":"","originalBlock":"","originalLine":" - [ ] bar","tags":[],"title":["bar"]}]}""")
+ ]
+
+
filePath : Test
filePath =
describe "filePath"
@@ -974,27 +1254,51 @@ notes =
]
-originalText : Test
-originalText =
- describe "originalText"
+originalBlock : Test
+originalBlock =
+ describe "originalBlock"
+ [ test "retains the original line text for a single line task" <|
+ \() ->
+ "- [X] the @due(2019-12-30) task @completed(2020-01-01) title "
+ |> Parser.run TaskItemHelpers.basicParser
+ |> Result.map TaskItem.originalBlock
+ |> Expect.equal (Ok "- [X] the @due(2019-12-30) task @completed(2020-01-01) title")
+ , test "retains the original line including any indented notes" <|
+ \() ->
+ "- [ ] foo\n some notes\n"
+ |> Parser.run TaskItemHelpers.basicParser
+ |> Result.map TaskItem.originalBlock
+ |> Expect.equal (Ok "- [ ] foo\n some notes")
+ , test "retains the original line including any subtasks" <|
+ \() ->
+ "- [ ] foo\n some notes\n - [ ] a subtask\n\n more notes\n - [ ]invalid subtask\n"
+ |> Parser.run TaskItemHelpers.basicParser
+ |> Result.map TaskItem.originalBlock
+ |> Expect.equal (Ok "- [ ] foo\n some notes\n - [ ] a subtask\n\n more notes\n - [ ]invalid subtask")
+ ]
+
+
+originalLine : Test
+originalLine =
+ describe "originalLine"
[ test "retains the original line text" <|
\() ->
"- [X] the @due(2019-12-30) task @completed(2020-01-01) title "
|> Parser.run TaskItemHelpers.basicParser
- |> Result.map TaskItem.originalText
+ |> Result.map TaskItem.originalLine
|> Expect.equal (Ok "- [X] the @due(2019-12-30) task @completed(2020-01-01) title ")
, test "retains the original line text even with a '*' list marker" <|
\() ->
"* [X] the @due(2019-12-30) task @completed(2020-01-01) title "
|> Parser.run TaskItemHelpers.basicParser
- |> Result.map TaskItem.originalText
+ |> Result.map TaskItem.originalLine
|> Expect.equal (Ok "* [X] the @due(2019-12-30) task @completed(2020-01-01) title ")
, test "retains leading whitepace for the original line text for descendantTasks" <|
\() ->
"- [X] task\n \t - [ ] sub-task"
|> Parser.run TaskItemHelpers.basicParser
|> Result.map TaskItem.descendantTasks
- |> Result.map (List.map TaskItem.originalText)
+ |> Result.map (List.map TaskItem.originalLine)
|> Expect.equal (Ok [ " \t - [ ] sub-task" ])
]
@@ -1399,3 +1703,13 @@ updateFilePath =
|> TaskItem.filePath
|> Expect.equal "old/path"
]
+
+
+
+-- HELPERS
+
+
+taskItemPlus : String -> Int -> String -> TaskItem
+taskItemPlus file offset markdown =
+ Parser.run (TaskItem.parser DataviewTaskCompletion.NoCompletion file Nothing TagList.empty offset) markdown
+ |> Result.withDefault TaskItem.dummy
diff --git a/tests/TaskListTests.elm b/tests/TaskListTests.elm
index 6134815e..e595247f 100644
--- a/tests/TaskListTests.elm
+++ b/tests/TaskListTests.elm
@@ -2,14 +2,17 @@ module TaskListTests exposing (suite)
import DataviewTaskCompletion
import Expect
+import Helpers.DecodeHelpers as DecodeTestHelpers
import Helpers.TaskHelpers as TaskHelpers
import Helpers.TaskItemHelpers as TaskItemHelpers
import Helpers.TaskListHelpers as TaskListHelpers
+import MarkdownFile exposing (MarkdownFile)
import Parser
import TagList
import TaskItem exposing (TaskItem)
-import TaskList
+import TaskList exposing (TaskList)
import Test exposing (..)
+import TsJson.Encode as TsEncode
suite : Test
@@ -17,14 +20,17 @@ suite =
concat
[ add
, combine
+ , decoder
+ , encoder
, filter
+ , fromList
+ , fromMarkdown
, map
- , parsing
- , replaceForFile
- , removeForFile
+ , markdownDiffs
+ , replaceTaskItems
, taskContainingId
, taskFromId
- , tasks
+ , toList
]
@@ -65,16 +71,62 @@ combine =
]
+decoder : Test
+decoder =
+ describe "decoder"
+ [ test "decodes an empty TaskList" <|
+ \() ->
+ "[]"
+ |> DecodeTestHelpers.runDecoder TaskList.decoder
+ |> .decoded
+ |> Expect.equal (Ok TaskList.empty)
+ , test "decodes a TaskList containing tasks" <|
+ \() ->
+ let
+ taskList : TaskList
+ taskList =
+ TaskList.empty
+ |> TaskList.add (taskItem "- [ ] foo")
+ |> TaskList.add (taskItem "- [ ] bar")
+ in
+ """[{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"bar"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] bar","originalLine":"- [ ] bar","tags":[],"title":["bar"]},"subFields":[]},{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[]}]"""
+ |> DecodeTestHelpers.runDecoder TaskList.decoder
+ |> .decoded
+ |> Expect.equal (Ok taskList)
+ ]
+
+
+encoder : Test
+encoder =
+ describe "encoder"
+ [ test "encodes an empty TaskList" <|
+ \() ->
+ TaskList.empty
+ |> TsEncode.runExample TaskList.encoder
+ |> .output
+ |> Expect.equal """[]"""
+ , test "encodes an TaskList containing tasks" <|
+ \() ->
+ TaskList.empty
+ |> TaskList.add (taskItem "- [ ] foo")
+ |> TaskList.add (taskItem "- [ ] bar")
+ |> TsEncode.runExample TaskList.encoder
+ |> .output
+ |> Expect.equal """[{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"bar"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] bar","originalLine":"- [ ] bar","tags":[],"title":["bar"]},"subFields":[]},{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[]}]"""
+ ]
+
+
filter : Test
filter =
describe "filter"
[ test "returns an empty TaskList if given an one" <|
\() ->
""
- |> Parser.run TaskListHelpers.basicParser
- |> Result.map (TaskList.filter (always True))
- |> Result.map TaskList.taskTitles
- |> Expect.equal (Ok [])
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.filter (always True)
+ |> TaskList.taskTitles
+ |> Expect.equal []
, test "filters a TaskList based on a TaskItem property" <|
\() ->
"""- [ ] foo
@@ -82,51 +134,45 @@ filter =
- [X] baz #tag2
- [X] boo
"""
- |> Parser.run TaskListHelpers.basicParser
- |> Result.map (TaskList.filter TaskItem.hasTags)
- |> Result.map TaskList.taskTitles
- |> Expect.equal (Ok [ "bar", "baz" ])
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.filter TaskItem.hasTags
+ |> TaskList.taskTitles
+ |> Expect.equal [ "bar", "baz" ]
]
-map : Test
-map =
- describe "map"
- [ test "returns an empty TaskList if given an one" <|
+fromList : Test
+fromList =
+ describe "fromList"
+ [ test "returns an empty TaskList if given an empty list" <|
\() ->
- ""
- |> Parser.run TaskListHelpers.basicParser
- |> Result.map (TaskList.map identity)
- |> Result.map TaskList.taskTitles
- |> Expect.equal (Ok [])
- , test "maps the contents of a TaskList throgh a function" <|
+ []
+ |> TaskList.fromList
+ |> Expect.equal TaskList.empty
+ , test "returns a TaskList containing the items in the list" <|
\() ->
- """- [ ] foo
-- [x] bar #tag1
-"""
- |> Parser.run (TaskList.parser DataviewTaskCompletion.NoCompletion "old/path" Nothing TagList.empty 0)
- |> Result.map (TaskList.map <| TaskItem.updateFilePath "old/path" "new/path")
- |> Result.map TaskList.topLevelTasks
- |> Result.map (List.map TaskItem.filePath)
- |> Expect.equal (Ok [ "new/path", "new/path" ])
+ [ taskItem "- [ ] foo", taskItem "- [ ] bar" ]
+ |> TaskList.fromList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "foo", "bar" ]
]
-parsing : Test
-parsing =
- describe "todo parsing"
+fromMarkdown : Test
+fromMarkdown =
+ describe "todo fromMarkdown"
[ test "parses an empty file" <|
\() ->
""
- |> Parser.run TaskListHelpers.basicParser
- |> Result.withDefault TaskList.empty
- |> TaskList.taskTitles
- |> Expect.equal []
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> Expect.equal TaskList.empty
, test "parses a single incomplete TaskList item" <|
\() ->
"- [ ] foo"
- |> Parser.run TaskListHelpers.basicParser
- |> Result.withDefault TaskList.empty
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
|> TaskList.taskTitles
|> Expect.equal [ "foo" ]
, test "parses a contiguous block of TaskList items" <|
@@ -135,8 +181,8 @@ parsing =
- [x] bar
- [X] baz
"""
- |> Parser.run TaskListHelpers.basicParser
- |> Result.withDefault TaskList.empty
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
|> TaskList.taskTitles
|> Expect.equal [ "foo", "bar", "baz" ]
, test "parses non contiguous TaskList items" <|
@@ -149,8 +195,8 @@ parsing =
- [X] baz
"""
- |> Parser.run TaskListHelpers.basicParser
- |> Result.withDefault TaskList.empty
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
|> TaskList.taskTitles
|> Expect.equal [ "foo", "bar", "baz" ]
, test "parses TaskList items with non-tasks interspersed" <|
@@ -164,8 +210,8 @@ not a task
- [X] baz
"""
- |> Parser.run TaskListHelpers.basicParser
- |> Result.withDefault TaskList.empty
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
|> TaskList.taskTitles
|> Expect.equal [ "foo", "bar", "baz" ]
, test "ignores indented tasks" <|
@@ -180,8 +226,8 @@ not a task
- [ ] a subtask
"""
- |> Parser.run TaskListHelpers.basicParser
- |> Result.withDefault TaskList.empty
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
|> TaskList.taskTitles
|> Expect.equal [ "foo", "bar", "baz" ]
, test "parses tasks when the first line of the file is blank" <|
@@ -191,8 +237,8 @@ not a task
- [x] bar
- [X] baz
"""
- |> Parser.run TaskListHelpers.basicParser
- |> Result.withDefault TaskList.empty
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
|> TaskList.taskTitles
|> Expect.equal [ "foo", "bar", "baz" ]
, test "parses tasks ignoring any that don't have a title" <|
@@ -203,22 +249,22 @@ not a task
- [x] bar
- [X] baz
"""
- |> Parser.run TaskListHelpers.basicParser
- |> Result.withDefault TaskList.empty
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
|> TaskList.taskTitles
|> Expect.equal [ "foo", "bar", "baz" ]
, test "parses tasks when the last line is a task and has NO line ending" <|
\() ->
"- [ ] foo\n- [x] bar"
- |> Parser.run TaskListHelpers.basicParser
- |> Result.withDefault TaskList.empty
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
|> TaskList.taskTitles
|> Expect.equal [ "foo", "bar" ]
, test "parses tasks when the last line is a non-task and has a line ending" <|
\() ->
"- [ ] foo\n- [x] bar\n\n## Log\n"
- |> Parser.run TaskListHelpers.basicParser
- |> Result.withDefault TaskList.empty
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
|> TaskList.taskTitles
|> Expect.equal [ "foo", "bar" ]
, test "parses ids consiting of the filePath and line number" <|
@@ -231,18 +277,19 @@ not a task
- [X] baz
"""
- |> Parser.run (TaskList.parser DataviewTaskCompletion.NoCompletion "file_a" Nothing TagList.empty 0)
- |> Result.withDefault TaskList.empty
+ |> basicMarkdown "file_a"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
|> TaskList.taskIds
|> Expect.equal [ "4275677999:1", "4275677999:4", "4275677999:6" ]
- , test "adds frontmatter tags to the tasks" <|
+ , test "adds frontmatter tags to all the tasks" <|
\() ->
"""- [ ] foo
- [x] bar
"""
- |> Parser.run (TaskList.parser DataviewTaskCompletion.NoCompletion "file_a" Nothing (TagList.fromList [ "fm_tag1", "fm_tag2" ]) 0)
- |> Result.withDefault TaskList.empty
- |> TaskList.tasks
+ |> basicMarkdown "file_a"
+ |> (\md -> { md | frontMatterTags = TagList.fromList [ "fm_tag1", "fm_tag2" ] })
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.toList
|> List.map TaskItem.tags
|> Expect.equal
[ TagList.fromList [ "fm_tag1", "fm_tag2" ]
@@ -251,55 +298,185 @@ not a task
]
-replaceForFile : Test
-replaceForFile =
- describe "replacing tasks from a chosen file"
- [ test "adds the tasks if the TaskList is empty" <|
+markdownDiffs : Test
+markdownDiffs =
+ describe "markdownDiffs"
+ [ test "returns an empty diff if both the original an updated files are empty" <|
\() ->
- TaskList.empty
- |> TaskList.replaceForFile "ignored"
- (TaskListHelpers.taskListFromFile "file a")
- |> TaskList.taskTitles
- |> List.sort
- |> Expect.equal (List.sort [ "c1", "c2" ])
- , test "adds the tasks if the list doesn't contain tasks from the file" <|
+ let
+ updatedMarkdown : MarkdownFile
+ updatedMarkdown =
+ basicMarkdown "a" ""
+ in
+ ""
+ |> basicMarkdown "a"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.markdownDiffs DataviewTaskCompletion.NoCompletion updatedMarkdown
+ |> Expect.equal { toAdd = [], toDelete = [] }
+ , test "returns an empty diff if both the original an updated files are the same" <|
+ \() ->
+ let
+ updatedMarkdown : MarkdownFile
+ updatedMarkdown =
+ basicMarkdown "a" "- [ ] foo"
+ in
+ "- [ ] foo"
+ |> basicMarkdown "a"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.markdownDiffs DataviewTaskCompletion.NoCompletion updatedMarkdown
+ |> Expect.equal { toAdd = [], toDelete = [] }
+ , test "returns a an empty diff if non-task line is edited" <|
+ \() ->
+ let
+ updatedMarkdown : MarkdownFile
+ updatedMarkdown =
+ basicMarkdown "a" "# title\n- [ ] foo"
+ in
+ "# LONGER title\n- [ ] foo"
+ |> basicMarkdown "a"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.markdownDiffs DataviewTaskCompletion.NoCompletion updatedMarkdown
+ |> Expect.equal { toAdd = [], toDelete = [] }
+ , test "returns a task to add if one has been added" <|
+ \() ->
+ let
+ updatedMarkdown : MarkdownFile
+ updatedMarkdown =
+ basicMarkdown "a" "# title\n- [ ] foo\n- [ ] bar"
+ in
+ "# title\n- [ ] foo"
+ |> basicMarkdown "a"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.markdownDiffs DataviewTaskCompletion.NoCompletion updatedMarkdown
+ |> Expect.equal
+ { toAdd = [ taskItemPlus "a" 2 "- [ ] bar" ]
+ , toDelete = []
+ }
+ , test "returns a task to remove if one has been removed" <|
+ \() ->
+ let
+ updatedMarkdown : MarkdownFile
+ updatedMarkdown =
+ basicMarkdown "a" "# title\n- [ ] foo"
+ in
+ "# title\n- [ ] foo\n- [ ] bar"
+ |> basicMarkdown "a"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.markdownDiffs DataviewTaskCompletion.NoCompletion updatedMarkdown
+ |> Expect.equal
+ { toAdd = []
+ , toDelete = [ taskItemPlus "a" 2 "- [ ] bar" ]
+ }
+ , test "returns tasks to remove and add if one has been edited" <|
+ \() ->
+ let
+ updatedMarkdown : MarkdownFile
+ updatedMarkdown =
+ basicMarkdown "a" "# title\n- [ ] xxx"
+ in
+ "# title\n- [ ] foo"
+ |> basicMarkdown "a"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.markdownDiffs DataviewTaskCompletion.NoCompletion updatedMarkdown
+ |> Expect.equal
+ { toAdd = [ taskItemPlus "a" 1 "- [ ] xxx" ]
+ , toDelete = [ taskItemPlus "a" 1 "- [ ] foo" ]
+ }
+ , test "returns deletes and adds if it has had a subtask added" <|
+ \() ->
+ let
+ updatedMarkdown : MarkdownFile
+ updatedMarkdown =
+ basicMarkdown "a" "# title\n- [ ] foo\n- [ ] bar\n - [ ] baz"
+ in
+ "# title\n- [ ] foo\n- [ ] bar"
+ |> basicMarkdown "a"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.markdownDiffs DataviewTaskCompletion.NoCompletion updatedMarkdown
+ |> Expect.equal
+ { toAdd = [ taskItemPlus "a" 2 "- [ ] bar\n - [ ] baz" ]
+ , toDelete = [ taskItemPlus "a" 2 "- [ ] bar" ]
+ }
+ , test "returns deletes and adds if it has had a subtask edited" <|
+ \() ->
+ let
+ updatedMarkdown : MarkdownFile
+ updatedMarkdown =
+ basicMarkdown "a" "# title\n- [ ] foo\n- [ ] bar\n - [ ] bazzer"
+ in
+ "# title\n- [ ] foo\n- [ ] bar\n - [ ] baz"
+ |> basicMarkdown "a"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.markdownDiffs DataviewTaskCompletion.NoCompletion updatedMarkdown
+ |> Expect.equal
+ { toAdd = [ taskItemPlus "a" 2 "- [ ] bar\n - [ ] bazzer" ]
+ , toDelete = [ taskItemPlus "a" 2 "- [ ] bar\n - [ ] baz" ]
+ }
+ , test "returns deletes and adds if it has had notes deleted" <|
+ \() ->
+ let
+ updatedMarkdown : MarkdownFile
+ updatedMarkdown =
+ basicMarkdown "a" "# title\n- [ ] foo"
+ in
+ "# title\n- [ ] foo\n some notes\n"
+ |> basicMarkdown "a"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.markdownDiffs DataviewTaskCompletion.NoCompletion updatedMarkdown
+ |> Expect.equal
+ { toAdd = [ taskItemPlus "a" 1 "- [ ] foo" ]
+ , toDelete = [ taskItemPlus "a" 1 "- [ ] foo\n some notes\n" ]
+ }
+ ]
+
+
+map : Test
+map =
+ describe "map"
+ [ test "returns an empty TaskList if given an one" <|
\() ->
- TaskListHelpers.taskListFromFileG
- |> TaskList.replaceForFile "ignored"
- (TaskListHelpers.taskListFromFile "file a")
+ ""
+ |> basicMarkdown ""
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> TaskList.map identity
|> TaskList.taskTitles
- |> List.sort
- |> Expect.equal (List.sort [ "g1", "g2", "c1", "c2" ])
- , test "replaces tasks from the file" <|
+ |> Expect.equal []
+ , test "maps the contents of a TaskList throgh a function" <|
\() ->
- TaskListHelpers.taskListFromFileG
- |> TaskList.append (TaskListHelpers.taskListFromFile "file a")
- |> TaskList.replaceForFile "file a"
- (TaskListHelpers.taskListFromNewFile "could be another file")
- |> TaskList.taskTitles
- |> List.sort
- |> Expect.equal (List.sort [ "n1", "n2", "g1", "g2" ])
+ """- [ ] foo
+- [x] bar #tag1
+"""
+ |> basicMarkdown "old/path"
+ |> TaskList.fromMarkdown DataviewTaskCompletion.NoCompletion
+ |> (TaskList.map <| TaskItem.updateFilePath "old/path" "new/path")
+ |> TaskList.topLevelTasks
+ |> List.map TaskItem.filePath
+ |> Expect.equal [ "new/path", "new/path" ]
]
-removeForFile : Test
-removeForFile =
- describe "removing tasks from a chosen file"
- [ test "has no effect if the TaskList is empty" <|
+replaceTaskItems : Test
+replaceTaskItems =
+ describe "replaceTaskItems"
+ [ test "does nothing if there are no replacement details" <|
\() ->
- TaskList.empty
- |> TaskList.removeForFile "a file"
+ TaskListHelpers.parsedTasks ( "a", Nothing, """
+- [ ] foo
+- [x] bar
+""" )
+ |> TaskList.replaceTaskItems []
|> TaskList.taskTitles
- |> List.sort
- |> Expect.equal (List.sort [])
- , test "remove tasks from the file" <|
+ |> Expect.equal [ "foo", "bar" ]
+ , test "replaces TaskItems if the id matches" <|
\() ->
- TaskListHelpers.taskListFromFileG
- |> TaskList.append (TaskListHelpers.taskListFromFile "file a")
- |> TaskList.removeForFile "file a"
+ TaskListHelpers.parsedTasks ( "a", Nothing, """
+- [ ] foo
+- [x] bar
+""" )
+ |> TaskList.replaceTaskItems [ ( "3826002220:3", taskItem "- [ ] baz" ) ]
|> TaskList.taskTitles
|> List.sort
- |> Expect.equal (List.sort [ "g1", "g2" ])
+ |> Expect.equal (List.sort [ "foo", "baz" ])
]
@@ -377,13 +554,13 @@ taskFromId =
]
-tasks : Test
-tasks =
+toList : Test
+toList =
describe "tasks"
[ test "returns an empty list if there are no tasks" <|
\() ->
TaskListHelpers.parsedTasks ( "a", Nothing, "" )
- |> TaskList.tasks
+ |> TaskList.toList
|> Expect.equal []
, test "returns a list of all tasks and subtasks" <|
\() ->
@@ -391,7 +568,7 @@ tasks =
- [ ] g1
- [x] subtask complete
""" )
- |> TaskList.tasks
+ |> TaskList.toList
|> List.map TaskItem.title
|> Expect.equal [ "g1", "subtask complete" ]
]
@@ -401,6 +578,22 @@ tasks =
-- HELPERS
+basicMarkdown : String -> String -> MarkdownFile
+basicMarkdown path body =
+ { filePath = path
+ , fileDate = Nothing
+ , frontMatterTags = TagList.empty
+ , bodyOffset = 0
+ , body = body
+ }
+
+
+taskItemPlus : String -> Int -> String -> TaskItem
+taskItemPlus file offset markdown =
+ Parser.run (TaskItem.parser DataviewTaskCompletion.NoCompletion file Nothing TagList.empty offset) markdown
+ |> Result.withDefault TaskItem.dummy
+
+
taskItem : String -> TaskItem
taskItem markdown =
Parser.run TaskItemHelpers.basicParser markdown
diff --git a/tests/Worker/InteropDefinitionsTests.elm b/tests/Worker/InteropDefinitionsTests.elm
new file mode 100644
index 00000000..48a8c678
--- /dev/null
+++ b/tests/Worker/InteropDefinitionsTests.elm
@@ -0,0 +1,169 @@
+module Worker.InteropDefinitionsTests exposing (suite)
+
+import Expect
+import Helpers.DecodeHelpers as DecodeHelpers
+import Helpers.TaskItemHelpers exposing (safeTaskItem)
+import TagList
+import TaskList
+import Test exposing (..)
+import TsJson.Encode as TsEncode
+import Worker.InteropDefinitions as InteropDefinitions exposing (interop)
+
+
+suite : Test
+suite =
+ concat
+ [ fromElmTests
+ , toElmTests
+ ]
+
+
+fromElmTests : Test
+fromElmTests =
+ describe "interop.fromElm (encoding)"
+ [ test "encodes AllTasksLoaded" <|
+ \() ->
+ InteropDefinitions.AllTasksLoaded
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"allTasksLoaded"}"""
+ , test "encodes AllTaskItems with an empty TaskList" <|
+ \() ->
+ TaskList.empty
+ |> InteropDefinitions.AllTaskItems
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"allTaskItems","data":[]}"""
+ , test "encodes AllTaskItems with an non-empty TaskList" <|
+ \() ->
+ [ safeTaskItem "- [ ] foo", safeTaskItem "- [ ] bar" ]
+ |> TaskList.fromList
+ |> InteropDefinitions.AllTaskItems
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"allTaskItems","data":[{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[]},{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"bar"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] bar","originalLine":"- [ ] bar","tags":[],"title":["bar"]},"subFields":[]}]}"""
+ , test "encodes TasksAdded with an empty TaskList" <|
+ \() ->
+ TaskList.empty
+ |> InteropDefinitions.TasksAdded
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"tasksAdded","data":[]}"""
+ , test "encodes TasksAdded with an non-empty TaskList" <|
+ \() ->
+ [ safeTaskItem "- [ ] foo", safeTaskItem "- [ ] bar" ]
+ |> TaskList.fromList
+ |> InteropDefinitions.TasksAdded
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"tasksAdded","data":[{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[]},{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"bar"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] bar","originalLine":"- [ ] bar","tags":[],"title":["bar"]},"subFields":[]}]}"""
+ , test "encodes TasksDeleted with no TaskItems" <|
+ \() ->
+ []
+ |> InteropDefinitions.TasksDeleted
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"tasksDeleted","data":[]}"""
+ , test "encodes TasksDeleted with some TaskItems" <|
+ \() ->
+ [ safeTaskItem "- [ ] foo", safeTaskItem "- [ ] bar" ]
+ |> InteropDefinitions.TasksDeleted
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"tasksDeleted","data":["2166136261:1","2166136261:1"]}"""
+ , test "encodes TasksDeletedAndAdded with no TaskItems" <|
+ \() ->
+ ( [], [] )
+ |> InteropDefinitions.TasksDeletedAndAdded
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"tasksDeletedAndAdded","data":[[],[]]}"""
+ , test "encodes TasksDeletedAndAdded with some TaskItems" <|
+ \() ->
+ ( [ "taskId:1", "taskId:2" ], [ safeTaskItem "- [ ] baz" ] )
+ |> InteropDefinitions.TasksDeletedAndAdded
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"tasksDeletedAndAdded","data":[["taskId:1","taskId:2"],[{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"baz"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] baz","originalLine":"- [ ] baz","tags":[],"title":["baz"]},"subFields":[]}]]}"""
+ , test "encodes TasksUpdated with no TaskItems" <|
+ \() ->
+ []
+ |> InteropDefinitions.TasksUpdated
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"tasksUpdated","data":[]}"""
+ , test "encodes TasksUpdated with some TaskItems" <|
+ \() ->
+ [ ( "foo", safeTaskItem "- [ ] foo" ), ( "bar", safeTaskItem "- [ ] bar" ) ]
+ |> InteropDefinitions.TasksUpdated
+ |> TsEncode.runExample interop.fromElm
+ |> .output
+ |> Expect.equal """{"tag":"tasksUpdated","data":[["foo",{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"foo"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] foo","originalLine":"- [ ] foo","tags":[],"title":["foo"]},"subFields":[]}],["bar",{"fields":{"autoComplete":{"tag":"NotSpecifed"},"completion":{"tag":"Incomplete"},"contents":[{"tag":"Word","data":"bar"}],"dueFile":null,"dueTag":{"tag":"NotSet"},"filePath":"","lineNumber":1,"notes":"","originalBlock":"- [ ] bar","originalLine":"- [ ] bar","tags":[],"title":["bar"]},"subFields":[]}]]}"""
+ ]
+
+
+toElmTests : Test
+toElmTests =
+ describe "interop.toElm (decoding)"
+ [ test "decodes allMarkdownLoaded" <|
+ \() ->
+ """{"tag":"allMarkdownLoaded","data":{}}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal (Ok <| InteropDefinitions.AllMarkdownLoaded)
+ , test "decodes viewInitialized" <|
+ \() ->
+ """{"tag":"viewInitialized"}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal (Ok InteropDefinitions.ViewInitialized)
+ , test "decodes fileAdded data" <|
+ \() ->
+ """{"tag":"fileAdded","data":{"filePath":"a path","fileDate":"a date","fileContents":"---\\ntags: [ a_tag ]\\n---\\nsome contents"}}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal
+ (Ok <|
+ InteropDefinitions.FileAdded
+ { filePath = "a path"
+ , fileDate = Just "a date"
+ , frontMatterTags = TagList.fromList [ "a_tag" ]
+ , bodyOffset = 3
+ , body = "some contents"
+ }
+ )
+ , test "decodes fileDeleted data" <|
+ \() ->
+ """{"tag":"fileDeleted","data":"a path"}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal (Ok <| InteropDefinitions.FileDeleted "a path")
+ , test "decodes fileModified data" <|
+ \() ->
+ """{"tag":"fileModified","data":{"filePath":"a path","fileDate":"a date","frontMatterTags":["a_tag"],"fileContents":"---\\ntags: [ a_tag ]\\n---\\nsome contents"}}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal
+ (Ok <|
+ InteropDefinitions.FileModified
+ { filePath = "a path"
+ , fileDate = Just "a date"
+ , frontMatterTags = TagList.fromList [ "a_tag" ]
+ , bodyOffset = 3
+ , body = "some contents"
+ }
+ )
+ , test "decodes fileRenamed data" <|
+ \() ->
+ """{"tag":"fileRenamed","data":{"oldPath":"the old path","newPath":"the new path"}}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Expect.equal (Ok <| InteropDefinitions.FileRenamed ( "the old path", "the new path" ))
+ , test "fails to decode data with an unknown tag" <|
+ \() ->
+ """{"tag":"xxxxx","data":{"filePath":"a path","fileDate":"a date","fileContents":"some contents"}}"""
+ |> DecodeHelpers.runDecoder interop.toElm
+ |> .decoded
+ |> Result.toMaybe
+ |> Expect.equal Nothing
+ ]
diff --git a/tests/Worker/SessionTests.elm b/tests/Worker/SessionTests.elm
new file mode 100644
index 00000000..b0b42f30
--- /dev/null
+++ b/tests/Worker/SessionTests.elm
@@ -0,0 +1,204 @@
+module Worker.SessionTests exposing (suite)
+
+-- import BoardConfig
+-- import Card
+-- import Column
+-- import Column.Completed as CompletedColumn
+-- import Columns
+-- import DragAndDrop.DragData as DragData
+-- import DragAndDrop.DragTracker as DragTracker
+-- import Filter
+-- import GlobalSettings exposing (GlobalSettings)
+-- import Helpers.FilterHelpers as FilterHelpers
+-- import Helpers.TaskItemHelpers as TaskItemHelpers
+-- import Parser
+-- import SafeZipper
+
+import DataviewTaskCompletion
+import Expect
+import Helpers.TaskListHelpers as TaskListHelpers
+import TaskItem
+import TaskList
+import Test exposing (..)
+import Worker.InteropDefinitions exposing (Flags)
+import Worker.Session as Session
+
+
+
+-- import Time
+
+
+suite : Test
+suite =
+ concat
+ [ addTaskList
+ , default
+ , finishAdding
+ , fromFlags
+ , removeTaskItems
+ , replaceTaskList
+ ]
+
+
+addTaskList : Test
+addTaskList =
+ describe "addTaskList"
+ [ test "can add a tasklist to a new session" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "a1", "a2" ]
+ , test "can add tasklists onto a session which already contains one" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.addTaskList TaskListHelpers.taskListFromFileG
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "a1", "a2", "g1", "g2" ]
+ , test "can add tasklist even if previously finished adding" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.finishAdding
+ |> Session.addTaskList TaskListHelpers.taskListFromFileG
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "a1", "a2", "g1", "g2" ]
+ ]
+
+
+default : Test
+default =
+ describe "default"
+ [ test "has an empty task list" <|
+ \() ->
+ Session.default
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal []
+ , test "has DataviewTaskCompletion.Text completion" <|
+ \() ->
+ Session.default
+ |> Session.dataviewTaskCompletion
+ |> Expect.equal (DataviewTaskCompletion.Text "completion")
+ ]
+
+
+finishAdding : Test
+finishAdding =
+ describe "finishAdding"
+ [ test "results in an empty tasklist if none have been added to the default Session" <|
+ \() ->
+ Session.default
+ |> Session.finishAdding
+ |> Session.taskList
+ |> Expect.equal TaskList.empty
+ , test "if Loading, there are the loaded tasks in the list" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.finishAdding
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "a1", "a2" ]
+ , test "if Loaded, stays Loaded" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.finishAdding
+ |> Session.finishAdding
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "a1", "a2" ]
+ ]
+
+
+fromFlags : Test
+fromFlags =
+ describe "fromFlags"
+ [ test "copies the dataviewTaskCompletion value from the flags" <|
+ \() ->
+ { exampleFlags | dataviewTaskCompletion = DataviewTaskCompletion.Emoji }
+ |> Session.fromFlags
+ |> Session.dataviewTaskCompletion
+ |> Expect.equal DataviewTaskCompletion.Emoji
+ ]
+
+
+removeTaskItems : Test
+removeTaskItems =
+ describe "removeTaskItems"
+ [ test "does nothing if there are no taskItems in the Session" <|
+ \() ->
+ Session.default
+ |> Session.removeTaskItems []
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal []
+ , test "removes tasks with the given ids whilst loading tasklists" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.addTaskList TaskListHelpers.taskListFromFileG
+ |> Session.removeTaskItems [ "3826002220:2", "3792446982:3" ]
+ |> Session.taskList
+ |> TaskList.toList
+ |> List.map TaskItem.id
+ |> Expect.equal [ "3826002220:3", "3792446982:2" ]
+ , test "removes tasks with the given ids if finished loading tasklists" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.addTaskList TaskListHelpers.taskListFromFileG
+ |> Session.finishAdding
+ |> Session.removeTaskItems [ "3826002220:2", "3792446982:3" ]
+ |> Session.taskList
+ |> TaskList.toList
+ |> List.map TaskItem.id
+ |> Expect.equal [ "3826002220:3", "3792446982:2" ]
+ ]
+
+
+replaceTaskList : Test
+replaceTaskList =
+ describe "replaceTaskList"
+ [ test "does nothing if a new session (state -> Waiting)" <|
+ \() ->
+ Session.default
+ |> Session.replaceTaskList TaskListHelpers.taskListFromFileA
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal []
+ , test "replaces all tasks with those given whilst loading tasklists" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.addTaskList TaskListHelpers.taskListFromFileG
+ |> Session.replaceTaskList (TaskListHelpers.taskListFromNewFile "path")
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "n1", "n2" ]
+ , test "replaces all tasks with those given whilst loading tasklists even if finished adding" <|
+ \() ->
+ Session.default
+ |> Session.addTaskList TaskListHelpers.taskListFromFileA
+ |> Session.addTaskList TaskListHelpers.taskListFromFileG
+ |> Session.finishAdding
+ |> Session.replaceTaskList (TaskListHelpers.taskListFromNewFile "path")
+ |> Session.taskList
+ |> TaskList.taskTitles
+ |> Expect.equal [ "n1", "n2" ]
+ ]
+
+
+
+-- -- HELPERS
+
+
+exampleFlags : Flags
+exampleFlags =
+ { dataviewTaskCompletion = DataviewTaskCompletion.NoCompletion
+ }
diff --git a/typescript/main.ts b/typescript/main.ts
index 9a4ad8bb..94efe835 100644
--- a/typescript/main.ts
+++ b/typescript/main.ts
@@ -1,24 +1,148 @@
-import { App, Modal, Notice, Plugin, PluginSettingTab, Setting, addIcon, normalizePath } from 'obsidian';
+import {
+ App,
+ Modal,
+ Notice,
+ Plugin,
+ PluginSettingTab,
+ Setting,
+ TAbstractFile,
+ TFile,
+ addIcon,
+ moment,
+ normalizePath } from 'obsidian';
import { CardBoardView, VIEW_TYPE_CARD_BOARD } from './view';
-import { CardBoardPluginSettings, CardBoardPluginSettingsPostV11 } from './types';
+import { CardBoardPluginSettings, CardBoardPluginSettingsPostV11, TaskItem } from './types';
+import { Elm, ElmApp, Flags } from '../src/Worker';
+import { FileFilter } from './fileFilter'
+import { getDateFromFile, IPeriodicNoteSettings } from 'obsidian-daily-notes-interface';
export default class CardBoardPlugin extends Plugin {
private commandIds: string[] = [];
- settings: CardBoardPluginSettings;
+ private fileFilter: FileFilter;
+ private worker: ElmApp;
+ settings: CardBoardPluginSettings;
async onload() {
- console.log('loading CardBoard plugin');
+ console.log('CardBoard: loading plugin');
await this.loadSettings();
- this.app.workspace.onLayoutReady(this.onLayoutReady.bind(this));
- }
- async onLayoutReady() {
this.registerView(
VIEW_TYPE_CARD_BOARD,
(leaf) => new CardBoardView(this, leaf)
);
+ this.app.workspace.onLayoutReady(this.onLayoutReady.bind(this));
+ }
+
+ async onLayoutReady() {
+ console.debug("Cardboard: [main] onLayoutReady");
+
+ const globalSettings : any = this.settings?.data.globalSettings;
+
+ if ((!(globalSettings === undefined)) && globalSettings.hasOwnProperty('filters')) {
+ this.fileFilter = new FileFilter(globalSettings.filters);
+ } else {
+ this.fileFilter = new FileFilter([]);
+ }
+
+ // @ts-ignore
+ const dataviewSettings = this.app.plugins.getPlugin("dataview")?.settings
+
+ const workerFlags:Flags = {
+ dataviewTaskCompletion: {
+ taskCompletionTracking: dataviewSettings === undefined ? true : dataviewSettings['taskCompletionTracking'],
+ taskCompletionUseEmojiShorthand: dataviewSettings === undefined ? false : dataviewSettings['taskCompletionUseEmojiShorthand'],
+ taskCompletionText: dataviewSettings === undefined ? "completion" : dataviewSettings['taskCompletionText']
+ }
+ };
+
+ console.debug("CardBoard: [main] Elm.Worker.init");
+ // @ts-ignore
+ this.worker = Elm.Worker.init({
+ flags: workerFlags
+ });
+
+ const that = this;
+
+ this.worker.ports.interopFromElm.subscribe((fromElm) => {
+ switch (fromElm.tag) {
+ case "allTaskItems":
+ that.handleAllTaskItems(fromElm.data);
+ break;
+ case "allTasksLoaded":
+ that.handleAllTasksLoaded();
+ break;
+ case "tasksAdded":
+ that.handleTasksAdded(fromElm.data);
+ break;
+ case "tasksDeleted":
+ that.handleTasksDeleted(fromElm.data);
+ break;
+ case "tasksDeletedAndAdded":
+ that.handleTasksDeletedAndAdded(fromElm.data);
+ break;
+ case "tasksUpdated":
+ that.handleTasksUpdated(fromElm.data);
+ break;
+ }
+ });
+
+ this.registerEvent(this.app.vault.on("create",
+ (file) => this.handleFileCreated(file)));
+
+ this.registerEvent(this.app.vault.on("delete",
+ (file) => this.handleFileDeleted(file)));
+
+ this.registerEvent(this.app.vault.on("modify",
+ (file) => this.handleFileModified(file)));
+
+ this.registerEvent(this.app.vault.on("rename",
+ (file, oldPath) => this.handleFileRenamed(file, oldPath)));
+
+ const markdownFiles = this.app.vault.getMarkdownFiles();
+ const filteredFiles = markdownFiles.filter((file) => this.fileFilter.isAllowed(file.path));
+
+ for (const file of filteredFiles) {
+ const fileDate = this.formattedFileDate(file);
+ const fileContents = await this.app.vault.cachedRead(file);
+
+ this.worker.ports.interopToElm.send({
+ tag: "fileAdded",
+ data: {
+ filePath: file.path,
+ fileDate: fileDate,
+ fileContents: fileContents
+ }
+ });
+ }
+
+ console.debug("CardBoard: [main] toWorker <- allMarkdownLoaded");
+ this.worker.ports.interopToElm.send({
+ tag: "allMarkdownLoaded",
+ data: { }
+ });
+
+ console.debug("CardBoard: " + markdownFiles.length + " markdown files in vault.");
+ console.log("CardBoard: " + filteredFiles.length + " files scanned for tasks.");
+ }
+
+
+ async handleAllTaskItems(taskItems: TaskItem[]) {
+ console.debug("CardBoard: [main] fromWorker -> allTasksItems");
+
+ const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_CARD_BOARD);
+
+ for (const leaf of leaves) {
+ if (leaf.view instanceof CardBoardView) {
+ leaf.view.taskItemsRefreshed(taskItems);
+ }
+ }
+ }
+
+ async handleAllTasksLoaded() {
+ console.debug("CardBoard: [main] fromWorker -> allTasksLoaded");
+
addIcon("card-board",
'' +
'' +
@@ -31,6 +155,60 @@ export default class CardBoardPlugin extends Plugin {
this.addCommands();
}
+ async viewInitialized() {
+ console.debug("CardBoard: [main] toWorker <- viewInitialized");
+ this.worker?.ports.interopToElm.send({
+ tag: "viewInitialized"
+ });
+ }
+
+
+ async handleTasksAdded(taskItems : TaskItem[]) {
+ const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_CARD_BOARD);
+
+ for (const leaf of leaves) {
+ if (leaf.view instanceof CardBoardView) {
+ leaf.view.taskItemsAdded(taskItems);
+ }
+ }
+ }
+
+ async handleTasksDeleted(taskIds : string[]) {
+ console.debug("CardBoard: [main] fromWorker -> tasksDeleted");
+
+ const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_CARD_BOARD);
+
+ for (const leaf of leaves) {
+ if (leaf.view instanceof CardBoardView) {
+ leaf.view.taskItemsDeleted(taskIds);
+ }
+ }
+ }
+
+ async handleTasksDeletedAndAdded(toDeleteAndAdd : [string[], TaskItem[]]) {
+ console.debug("CardBoard: [main] fromWorker -> tasksDeletedAndAdded: (" + toDeleteAndAdd[0].length + ", " + toDeleteAndAdd[1].length + ")");
+
+ const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_CARD_BOARD);
+
+ for (const leaf of leaves) {
+ if (leaf.view instanceof CardBoardView) {
+ leaf.view.taskItemsDeletedAndAdded(toDeleteAndAdd);
+ }
+ }
+ }
+
+ async handleTasksUpdated(updateDetails : [string, TaskItem][]) {
+ console.debug("CardBoard: [main] fromWorker -> tasksUpdated: " + updateDetails.length);
+
+ const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_CARD_BOARD);
+
+ for (const leaf of leaves) {
+ if (leaf.view instanceof CardBoardView) {
+ leaf.view.taskItemsUpdated(updateDetails);
+ }
+ }
+ }
+
onunload() {
console.log('unloading CardBoard plugin');
this.app.workspace.detachLeavesOfType(VIEW_TYPE_CARD_BOARD);
@@ -114,4 +292,94 @@ export default class CardBoardPlugin extends Plugin {
this.app.vault.adapter.copy(pathToSettings, pathToSavedSettings);
}
}
+
+ async handleFileCreated(
+ file: TAbstractFile
+ ) {
+ if (file instanceof TFile) {
+ if (this.fileFilter.isAllowed(file.path)) {
+ const fileDate = this.formattedFileDate(file);
+ const fileContents = await this.app.vault.read(file);
+
+ this.worker.ports.interopToElm.send({
+ tag: "fileAdded",
+ data: {
+ filePath: file.path,
+ fileDate: fileDate,
+ fileContents: fileContents
+ }
+ });
+ }
+ }
+ }
+
+ async handleFileDeleted(
+ file: TAbstractFile
+ ) {
+ if (file instanceof TFile) {
+ this.worker.ports.interopToElm.send({
+ tag: "fileDeleted",
+ data: file.path
+ });
+ }
+ }
+
+ async handleFileModified(
+ file: TAbstractFile
+ ) {
+ if (file instanceof TFile) {
+ if (this.fileFilter.isAllowed(file.path)) {
+ const fileDate = this.formattedFileDate(file);
+ const fileContents = await this.app.vault.read(file);
+
+ this.worker.ports.interopToElm.send({
+ tag: "fileModified",
+ data: {
+ filePath: file.path,
+ fileDate: fileDate,
+ fileContents: fileContents
+ }
+ });
+ }
+ }
+ }
+
+ async handleFileRenamed(
+ file: TAbstractFile,
+ oldPath: string
+ ) {
+ let oldNew : [boolean, boolean] = [this.fileFilter.isAllowed(oldPath), this.fileFilter.isAllowed(file.path)];
+
+ switch(oldNew.join(",")) {
+ case 'false,true': {
+ this.handleFileCreated(file)
+ break;
+ }
+ case 'true,false': {
+ this.worker.ports.interopToElm.send({
+ tag: "fileDeleted",
+ data: oldPath
+ });
+ break;
+ }
+ case 'true,true': {
+ this.worker.ports.interopToElm.send({
+ tag: "fileRenamed",
+ data: {
+ oldPath: oldPath,
+ newPath: file.path
+ }
+ });
+ break;
+ }
+ }
+ }
+
+ // HELPERS
+
+ formattedFileDate(
+ file: TFile
+ ): string | null {
+ return getDateFromFile(file, "day")?.format('YYYY-MM-DD') || null;
+ }
}
diff --git a/typescript/types.ts b/typescript/types.ts
index 845acbc3..c0b210c1 100644
--- a/typescript/types.ts
+++ b/typescript/types.ts
@@ -1,5 +1,59 @@
export type CardBoardPluginSettings = CardBoardPluginSettingsPreV11 | CardBoardPluginSettingsPostV11;
+type AutoCompleteTag =
+ { tag : "FalseSpecified" | "NotSpecifed" | "TrueSpecified" }
+
+type AutoComplete =
+ { tag : "AutoCompleteTag", data : AutoCompleteTag }
+
+type CompletionTag =
+ { tag : "Completed" } |
+ { tag : "CompletedAt", data : number } |
+ { tag : "Incomplete" }
+
+type Completion =
+ { tag : "CompletedTag", data : number }
+
+type DueTag =
+ { tag : "NotSet" } |
+ { tag : "SetToDate", date : number } |
+ { tag : "SetToNone" }
+
+type Due = {
+ tag : "DueTag",
+ data : DueTag
+}
+
+type Tag = {
+ tag : "ObsidianTag",
+ data : string
+}
+
+type Word =
+ { tag : "Word", data : string }
+
+type Content = AutoComplete | Completion | Due | Tag | Word;
+
+export type TaskItemFields = {
+ autoComplete : AutoCompleteTag;
+ completion : CompletionTag;
+ contents : Content[];
+ dueFile : number | null;
+ dueTag : DueTag;
+ filePath : string;
+ lineNumber : number;
+ notes : string;
+ originalBlock : string;
+ originalLine : string;
+ tags : string[];
+ title : string[];
+}
+
+export type TaskItem = {
+ fields : TaskItemFields;
+ subFields : TaskItemFields[];
+}
+
export type CardBoardPluginSettingsPostV11 = {
data : {
boardConfigs : ({
diff --git a/typescript/view.ts b/typescript/view.ts
index 0757e286..87a400f0 100644
--- a/typescript/view.ts
+++ b/typescript/view.ts
@@ -16,7 +16,7 @@ import {
import { Elm, ElmApp, Flags } from '../src/Main';
import CardBoardPlugin from './main';
-import { CardBoardPluginSettingsPostV11 } from './types';
+import { CardBoardPluginSettingsPostV11, TaskItem } from './types';
import { getDateFromFile, IPeriodicNoteSettings } from 'obsidian-daily-notes-interface';
import { FileFilter } from './fileFilter'
import { Scrollable } from './scrollable'
@@ -107,7 +107,7 @@ export class CardBoardView extends ItemView {
that.handleDisplayTaskMarkdown(fromElm.data);
break;
case "elmInitialized":
- that.handleElmInitialized();
+ that.handleElmInitialized(fromElm.data);
break;
case "openTaskSourceFile":
that.handleOpenTaskSourceFile(fromElm.data);
@@ -133,18 +133,6 @@ export class CardBoardView extends ItemView {
this.registerEvent(this.app.workspace.on("active-leaf-change",
(leaf) => this.handleActiveLeafChange(leaf)));
- this.registerEvent(this.app.vault.on("create",
- (file) => this.handleFileCreated(file)));
-
- this.registerEvent(this.app.vault.on("delete",
- (file) => this.handleFileDeleted(file)));
-
- this.registerEvent(this.app.vault.on("modify",
- (file) => this.handleFileModified(file)));
-
- this.registerEvent(this.app.vault.on("rename",
- (file, oldPath) => this.handleFileRenamed(file, oldPath)));
-
// @ts-ignore
this.registerEvent(this.app.vault.on("config-changed",
() => this.handleConfigChanged()));
@@ -202,7 +190,7 @@ export class CardBoardView extends ItemView {
data: {
filePath: string,
lineNumber: number,
- originalText: string}
+ originalLine: string}
) {
const file = this.app.vault.getAbstractFileByPath(data.filePath)
@@ -210,7 +198,7 @@ export class CardBoardView extends ItemView {
const markdown = await this.vault.read(file)
const markdownLines = markdown.split(/\r?\n/)
- if (markdownLines[data.lineNumber - 1].includes(data.originalText)) {
+ if (markdownLines[data.lineNumber - 1].includes(data.originalLine)) {
markdownLines[data.lineNumber - 1] = markdownLines[data.lineNumber - 1].replace(/^(.*)$/, "$1")
this.vault.modify(file, markdownLines.join("\n"))
}
@@ -279,28 +267,10 @@ export class CardBoardView extends ItemView {
})
}
- async handleElmInitialized() {
- const markdownFiles = this.vault.getMarkdownFiles();
- const filteredFiles = markdownFiles.filter((file) => this.fileFilter.isAllowed(file.path));
-
- for (const file of filteredFiles) {
- const fileDate = this.formattedFileDate(file);
- const fileContents = await this.vault.cachedRead(file);
-
- this.elm.ports.interopToElm.send({
- tag: "fileAdded",
- data: {
- filePath: file.path,
- fileDate: fileDate,
- fileContents: fileContents
- }
- });
- }
+ async handleElmInitialized(uniqueId : string) {
+ console.debug("CardBoard: [view] fromView -> elmInitialised");
- this.elm.ports.interopToElm.send({
- tag: "allMarkdownLoaded",
- data: { }
- });
+ this.plugin.viewInitialized();
}
@@ -308,7 +278,7 @@ export class CardBoardView extends ItemView {
data: {
filePath: string,
lineNumber: number,
- originalText: string
+ originalLine: string
}
) {
await this.openOrSwitchWithHighlight(this.app, data.filePath, data.lineNumber);
@@ -528,10 +498,53 @@ export class CardBoardView extends ItemView {
});
}
+ taskItemsRefreshed(taskItems: TaskItem[]) {
+ console.debug("CardBoard: [view] toView <- taskItemsRefreshed: " + taskItems.length);
+
+ this.elm.ports.interopToElm.send({
+ tag: "taskItemsRefreshed",
+ data: taskItems
+ });
+ }
+
+ taskItemsAdded(taskItems: TaskItem[]) {
+ this.elm.ports.interopToElm.send({
+ tag: "taskItemsAdded",
+ data: taskItems
+ });
+ }
+
+ taskItemsDeleted(taskIds: string[]) {
+ console.debug("CardBoard: [view] toView <- taskItemsDeleted: " + taskIds.length);
+
+ this.elm.ports.interopToElm.send({
+ tag: "taskItemsDeleted",
+ data: taskIds
+ });
+ }
+
+ taskItemsDeletedAndAdded(toDeleteAndAdd : [string[], TaskItem[]]) {
+ console.debug("CardBoard: [view] toView <- taskItemsDeletedAndAdded: (" + toDeleteAndAdd[0].length + ", " + toDeleteAndAdd[1].length + ")");
+
+ this.elm.ports.interopToElm.send({
+ tag: "taskItemsDeletedAndAdded",
+ data: toDeleteAndAdd
+ });
+ }
+
+ taskItemsUpdated(updateDetails : [string, TaskItem][]) {
+ console.debug("CardBoard: [view] toView <- taskItemsUpdated: " + updateDetails.length);
+
+ this.elm.ports.interopToElm.send({
+ tag: "taskItemsUpdated",
+ data: updateDetails
+ });
+ }
+
async handleUpdateTasks(
data: {
filePath: string,
- tasks: { lineNumber: number, originalText: string, newText: string }[]
+ tasks: { lineNumber: number, originalLine: string, newText: string }[]
}) {
const file = this.app.vault.getAbstractFileByPath(data.filePath)
@@ -540,7 +553,7 @@ export class CardBoardView extends ItemView {
const markdownLines = markdown.split(/\r?\n/)
for (const item of data.tasks) {
- if (markdownLines[item.lineNumber - 1].includes(item.originalText)) {
+ if (markdownLines[item.lineNumber - 1].includes(item.originalLine)) {
markdownLines[item.lineNumber - 1] = item.newText
}
}
@@ -573,88 +586,6 @@ export class CardBoardView extends ItemView {
});
}
- async handleFileCreated(
- file: TAbstractFile
- ) {
- if (file instanceof TFile) {
- if (this.fileFilter.isAllowed(file.path)) {
- const fileDate = this.formattedFileDate(file);
- const fileContents = await this.vault.read(file);
-
- this.elm.ports.interopToElm.send({
- tag: "fileAdded",
- data: {
- filePath: file.path,
- fileDate: fileDate,
- fileContents: fileContents
- }
- });
- }
- }
- }
-
- async handleFileDeleted(
- file: TAbstractFile
- ) {
- if (file instanceof TFile) {
- this.elm.ports.interopToElm.send({
- tag: "fileDeleted",
- data: file.path
- });
- }
- }
-
- async handleFileModified(
- file: TAbstractFile
- ) {
- if (file instanceof TFile) {
- if (this.fileFilter.isAllowed(file.path)) {
- const fileDate = this.formattedFileDate(file);
- const fileContents = await this.vault.read(file);
-
- this.elm.ports.interopToElm.send({
- tag: "fileUpdated",
- data: {
- filePath: file.path,
- fileDate: fileDate,
- fileContents: fileContents
- }
- });
- }
- }
- }
-
- async handleFileRenamed(
- file: TAbstractFile,
- oldPath: string
- ) {
- let oldNew : [boolean, boolean] = [this.fileFilter.isAllowed(oldPath), this.fileFilter.isAllowed(file.path)];
-
- switch(oldNew.join(",")) {
- case 'false,true': {
- this.handleFileCreated(file)
- break;
- }
- case 'true,false': {
- this.elm.ports.interopToElm.send({
- tag: "fileDeleted",
- data: oldPath
- });
- break;
- }
- case 'true,true': {
- this.elm.ports.interopToElm.send({
- tag: "fileRenamed",
- data: {
- oldPath: oldPath,
- newPath: file.path
- }
- });
- break;
- }
- }
- }
-
// HELPERS
formattedFileDate(
diff --git a/webpack.config.js b/webpack.common.js
similarity index 60%
rename from webpack.config.js
rename to webpack.common.js
index f8d3e81d..63e47587 100644
--- a/webpack.config.js
+++ b/webpack.common.js
@@ -2,6 +2,9 @@ const path = require('path');
module.exports = {
entry: './typescript/main.ts',
+ performance: {
+ hints: false
+ },
output: {
path: path.resolve(__dirname, '.'),
libraryTarget: 'commonjs',
@@ -13,17 +16,6 @@ module.exports = {
test: /\.ts$/,
use: 'ts-loader',
},
- {
- test: [/\.elm$/],
- exclude: [/elm-stuff/, /node_modules/],
- use: [
- { loader: "elm-reloader" },
- {
- loader: "elm-webpack-loader",
- options: {}
- }
- ]
- },
]
},
externals: {
diff --git a/webpack.dev.js b/webpack.dev.js
new file mode 100644
index 00000000..2f5cb827
--- /dev/null
+++ b/webpack.dev.js
@@ -0,0 +1,23 @@
+const path = require('path');
+
+const { merge } = require("webpack-merge");
+const common = require("./webpack.common.js");
+
+module.exports = merge(common, {
+ mode: "development",
+ module: {
+ rules: [
+ {
+ test: [/\.elm$/],
+ exclude: [/elm-stuff/, /node_modules/],
+ use: [
+ { loader: "elm-reloader" },
+ {
+ loader: "elm-webpack-loader",
+ options: {}
+ }
+ ]
+ },
+ ]
+ },
+});
diff --git a/webpack.prod.js b/webpack.prod.js
new file mode 100644
index 00000000..f4a8a8a6
--- /dev/null
+++ b/webpack.prod.js
@@ -0,0 +1,53 @@
+const path = require("path");
+const { merge } = require("webpack-merge");
+const common = require("./webpack.common.js");
+const TerserPlugin = require("terser-webpack-plugin");
+
+module.exports = merge(common, {
+ mode: "production",
+ optimization: {
+ 'minimize': true,
+ minimizer: [new TerserPlugin({
+ terserOptions: {
+ compress: {
+ pure_funcs: [
+ 'console.info',
+ 'console.debug',
+ 'console.warn',
+ "F2",
+ "F3",
+ "F4",
+ "F5",
+ "F6",
+ "F7",
+ "F8",
+ "F9",
+ "A2",
+ "A3",
+ "A4",
+ "A5",
+ "A6",
+ "A7",
+ "A8",
+ "A9"
+
+ ]
+ }
+ }
+ })],
+ },
+ module: {
+ rules: [
+ {
+ test: [/\.elm$/],
+ exclude: [/elm-stuff/, /node_modules/],
+ use: [
+ {
+ loader: "elm-webpack-loader",
+ options: {}
+ }
+ ]
+ },
+ ]
+ },
+});