diff options
| -rw-r--r-- | package-lock.json | 781 | ||||
| -rw-r--r-- | package.json | 8 | ||||
| -rw-r--r-- | src/flatmatter.test.ts | 173 | ||||
| -rw-r--r-- | src/flatmatter.ts | 552 | ||||
| -rw-r--r-- | src/index.ts | 11 | ||||
| -rw-r--r-- | src/serializers/to_json.test.ts | 6 | ||||
| -rw-r--r-- | src/serializers/to_json.ts | 12 | ||||
| -rw-r--r-- | src/serializers/to_object.test.ts | 171 | ||||
| -rw-r--r-- | src/serializers/to_object.ts | 7 | ||||
| -rw-r--r-- | tsconfig.json | 1 | ||||
| -rw-r--r-- | tsup.config.ts | 14 |
11 files changed, 663 insertions, 1073 deletions
diff --git a/package-lock.json b/package-lock.json index 7bdb21d..29e12b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,11 @@ "name": "flatmatter", "version": "1.1.2", "license": "MIT", + "dependencies": { + "effect": "^3.12.10" + }, "devDependencies": { "@types/node": "^22.10.10", - "@vitest/coverage-istanbul": "^2.1.8", "@vitest/coverage-v8": "^2.1.8", "@vitest/ui": "^2.1.8", "tsup": "^8.3.5", @@ -36,148 +38,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", - "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", - "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.7", - "@babel/parser": "^7.26.7", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.26.7", - "@babel/types": "^7.26.7", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "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, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", - "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.5", - "@babel/types": "^7.26.5", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "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, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", @@ -198,38 +58,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", - "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/parser": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", - "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", + "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.7" + "@babel/types": "^7.26.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -238,44 +74,10 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", - "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.7", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.7", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/types": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", - "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", + "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", "dev": true, "license": "MIT", "dependencies": { @@ -818,9 +620,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz", - "integrity": "sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.6.tgz", + "integrity": "sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==", "cpu": [ "arm" ], @@ -832,9 +634,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.1.tgz", - "integrity": "sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.6.tgz", + "integrity": "sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==", "cpu": [ "arm64" ], @@ -846,9 +648,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.1.tgz", - "integrity": "sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.6.tgz", + "integrity": "sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==", "cpu": [ "arm64" ], @@ -860,9 +662,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.1.tgz", - "integrity": "sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.6.tgz", + "integrity": "sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==", "cpu": [ "x64" ], @@ -874,9 +676,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.1.tgz", - "integrity": "sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.6.tgz", + "integrity": "sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==", "cpu": [ "arm64" ], @@ -888,9 +690,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.1.tgz", - "integrity": "sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.6.tgz", + "integrity": "sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==", "cpu": [ "x64" ], @@ -902,9 +704,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.1.tgz", - "integrity": "sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.6.tgz", + "integrity": "sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==", "cpu": [ "arm" ], @@ -916,9 +718,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.1.tgz", - "integrity": "sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.6.tgz", + "integrity": "sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==", "cpu": [ "arm" ], @@ -930,9 +732,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.1.tgz", - "integrity": "sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.6.tgz", + "integrity": "sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==", "cpu": [ "arm64" ], @@ -944,9 +746,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.1.tgz", - "integrity": "sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.6.tgz", + "integrity": "sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==", "cpu": [ "arm64" ], @@ -958,9 +760,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.1.tgz", - "integrity": "sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.6.tgz", + "integrity": "sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==", "cpu": [ "loong64" ], @@ -972,9 +774,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.1.tgz", - "integrity": "sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.6.tgz", + "integrity": "sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==", "cpu": [ "ppc64" ], @@ -986,9 +788,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.1.tgz", - "integrity": "sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.6.tgz", + "integrity": "sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==", "cpu": [ "riscv64" ], @@ -1000,9 +802,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.1.tgz", - "integrity": "sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.6.tgz", + "integrity": "sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==", "cpu": [ "s390x" ], @@ -1014,9 +816,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz", - "integrity": "sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.6.tgz", + "integrity": "sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==", "cpu": [ "x64" ], @@ -1028,9 +830,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.1.tgz", - "integrity": "sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.6.tgz", + "integrity": "sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==", "cpu": [ "x64" ], @@ -1042,9 +844,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.1.tgz", - "integrity": "sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.6.tgz", + "integrity": "sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==", "cpu": [ "arm64" ], @@ -1056,9 +858,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.1.tgz", - "integrity": "sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.6.tgz", + "integrity": "sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==", "cpu": [ "ia32" ], @@ -1070,9 +872,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.1.tgz", - "integrity": "sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.6.tgz", + "integrity": "sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==", "cpu": [ "x64" ], @@ -1091,44 +893,19 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.12.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", - "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.20.0" } }, - "node_modules/@vitest/coverage-istanbul": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-2.1.8.tgz", - "integrity": "sha512-cSaCd8KcWWvgDwEJSXm0NEWZ1YTiJzjicKHy+zOEbUm0gjbbkz+qJf1p8q71uBzSlS7vdnZA8wRLeiwVE3fFTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@istanbuljs/schema": "^0.1.3", - "debug": "^4.3.7", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-instrument": "^6.0.3", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magicast": "^0.3.5", - "test-exclude": "^7.0.1", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "vitest": "2.1.8" - } - }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz", - "integrity": "sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.9.tgz", + "integrity": "sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1149,8 +926,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.8", - "vitest": "2.1.8" + "@vitest/browser": "2.1.9", + "vitest": "2.1.9" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1159,14 +936,14 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", - "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.8", - "@vitest/utils": "2.1.8", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" }, @@ -1175,13 +952,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", - "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.8", + "@vitest/spy": "2.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.12" }, @@ -1202,9 +979,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", - "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1215,13 +992,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", - "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.8", + "@vitest/utils": "2.1.9", "pathe": "^1.1.2" }, "funding": { @@ -1229,13 +1006,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", - "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.8", + "@vitest/pretty-format": "2.1.9", "magic-string": "^0.30.12", "pathe": "^1.1.2" }, @@ -1244,9 +1021,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", - "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1257,13 +1034,13 @@ } }, "node_modules/@vitest/ui": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-2.1.8.tgz", - "integrity": "sha512-5zPJ1fs0ixSVSs5+5V2XJjXLmNzjugHRyV11RqxYVR+oMcogZ9qTuSfKW+OcTV0JeFNznI83BNylzH6SSNJ1+w==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-2.1.9.tgz", + "integrity": "sha512-izzd2zmnk8Nl5ECYkW27328RbQ1nKvkm6Bb5DAaz1Gk59EbLkiCMa6OLT0NoaAYTjOFS6N+SMYW1nh4/9ljPiw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.8", + "@vitest/utils": "2.1.9", "fflate": "^0.8.2", "flatted": "^3.3.1", "pathe": "^1.1.2", @@ -1275,17 +1052,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "2.1.8" + "vitest": "2.1.9" } }, "node_modules/@vitest/utils": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", - "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.8", + "@vitest/pretty-format": "2.1.9", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, @@ -1353,39 +1130,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, "node_modules/bundle-require": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", @@ -1412,27 +1156,6 @@ "node": ">=8" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001695", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", - "integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, "node_modules/chai": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", @@ -1516,13 +1239,6 @@ "node": "^14.18.0 || >=16.10.0" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1573,12 +1289,14 @@ "dev": true, "license": "MIT" }, - "node_modules/electron-to-chromium": { - "version": "1.5.88", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.88.tgz", - "integrity": "sha512-K3C2qf1o+bGzbilTDCTBhTQcMS9KW60yTAaTeeXsfvQuTDDwlokLam/AdqlqcSy9u4UainDgsHV23ksXAOgamw==", - "dev": true, - "license": "ISC" + "node_modules/effect": { + "version": "3.12.11", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.12.11.tgz", + "integrity": "sha512-toXTZx8nNuS7By7EcCHmFT/uOVmkhlCFECsCAI+gNgDJhhMK8FJMjAyuqm5YEA58YJFZmMckuwiFs5Fzr8GiYg==", + "license": "MIT", + "dependencies": { + "fast-check": "^3.23.1" + } }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -1635,16 +1353,6 @@ "@esbuild/win32-x64": "0.24.2" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -1665,6 +1373,28 @@ "node": ">=12.0.0" } }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fdir": { "version": "6.4.3", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", @@ -1726,16 +1456,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -1757,16 +1477,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1811,23 +1521,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@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" - }, - "engines": { - "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", @@ -1898,39 +1591,6 @@ "node": ">=10" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -1976,14 +1636,11 @@ "license": "MIT" }, "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==", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } + "license": "ISC" }, "node_modules/magic-string": { "version": "0.30.17", @@ -2097,13 +1754,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2148,13 +1798,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/pathe": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", @@ -2203,9 +1846,9 @@ } }, "node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", + "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", "dev": true, "funding": [ { @@ -2284,6 +1927,22 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/readdirp": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", @@ -2309,9 +1968,9 @@ } }, "node_modules/rollup": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.1.tgz", - "integrity": "sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.6.tgz", + "integrity": "sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2325,32 +1984,32 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.32.1", - "@rollup/rollup-android-arm64": "4.32.1", - "@rollup/rollup-darwin-arm64": "4.32.1", - "@rollup/rollup-darwin-x64": "4.32.1", - "@rollup/rollup-freebsd-arm64": "4.32.1", - "@rollup/rollup-freebsd-x64": "4.32.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.32.1", - "@rollup/rollup-linux-arm-musleabihf": "4.32.1", - "@rollup/rollup-linux-arm64-gnu": "4.32.1", - "@rollup/rollup-linux-arm64-musl": "4.32.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.32.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.32.1", - "@rollup/rollup-linux-riscv64-gnu": "4.32.1", - "@rollup/rollup-linux-s390x-gnu": "4.32.1", - "@rollup/rollup-linux-x64-gnu": "4.32.1", - "@rollup/rollup-linux-x64-musl": "4.32.1", - "@rollup/rollup-win32-arm64-msvc": "4.32.1", - "@rollup/rollup-win32-ia32-msvc": "4.32.1", - "@rollup/rollup-win32-x64-msvc": "4.32.1", + "@rollup/rollup-android-arm-eabi": "4.34.6", + "@rollup/rollup-android-arm64": "4.34.6", + "@rollup/rollup-darwin-arm64": "4.34.6", + "@rollup/rollup-darwin-x64": "4.34.6", + "@rollup/rollup-freebsd-arm64": "4.34.6", + "@rollup/rollup-freebsd-x64": "4.34.6", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.6", + "@rollup/rollup-linux-arm-musleabihf": "4.34.6", + "@rollup/rollup-linux-arm64-gnu": "4.34.6", + "@rollup/rollup-linux-arm64-musl": "4.34.6", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.6", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.6", + "@rollup/rollup-linux-riscv64-gnu": "4.34.6", + "@rollup/rollup-linux-s390x-gnu": "4.34.6", + "@rollup/rollup-linux-x64-gnu": "4.34.6", + "@rollup/rollup-linux-x64-musl": "4.34.6", + "@rollup/rollup-win32-arm64-msvc": "4.34.6", + "@rollup/rollup-win32-ia32-msvc": "4.34.6", + "@rollup/rollup-win32-x64-msvc": "4.34.6", "fsevents": "~2.3.2" } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -2801,37 +2460,6 @@ "dev": true, "license": "MIT" }, - "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/vite": { "version": "5.4.14", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", @@ -2893,9 +2521,9 @@ } }, "node_modules/vite-node": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", - "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", "dev": true, "license": "MIT", "dependencies": { @@ -3346,19 +2974,19 @@ } }, "node_modules/vitest": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", - "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.8", - "@vitest/mocker": "2.1.8", - "@vitest/pretty-format": "^2.1.8", - "@vitest/runner": "2.1.8", - "@vitest/snapshot": "2.1.8", - "@vitest/spy": "2.1.8", - "@vitest/utils": "2.1.8", + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", "chai": "^5.1.2", "debug": "^4.3.7", "expect-type": "^1.1.0", @@ -3370,7 +2998,7 @@ "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.8", + "vite-node": "2.1.9", "why-is-node-running": "^2.3.0" }, "bin": { @@ -3385,8 +3013,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.8", - "@vitest/ui": "2.1.8", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", "happy-dom": "*", "jsdom": "*" }, @@ -3560,13 +3188,6 @@ "engines": { "node": ">=8" } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" } } } diff --git a/package.json b/package.json index 426fe10..8baa70a 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "url": "git+https://github.com/askonomm/flatmatter.git" }, "scripts": { - "bundle": "tsup src/index.ts", + "bundle": "tsup src/flatmatter.ts", "test": "vitest", "coverage": "vitest run --coverage" }, @@ -30,13 +30,15 @@ "node": ">=18" }, "devDependencies": { - "@vitest/coverage-istanbul": "^2.1.8", + "@types/node": "^22.10.10", "@vitest/coverage-v8": "^2.1.8", "@vitest/ui": "^2.1.8", "tsup": "^8.3.5", "typescript": "^5.7.3", - "@types/node": "^22.10.10", "vite": "^5.4.11", "vitest": "^2.1.8" + }, + "dependencies": { + "effect": "^3.12.10" } } diff --git a/src/flatmatter.test.ts b/src/flatmatter.test.ts index 9f00ef9..8e480fc 100644 --- a/src/flatmatter.test.ts +++ b/src/flatmatter.test.ts @@ -1,32 +1,185 @@ import FlatMatter from "./flatmatter.ts"; import { assert } from "vitest"; -import ToObject from "./serializers/to_object.ts"; + +test("Single-level configuration", () => { + const config = FlatMatter.config( + 'a: true\nb: false\nc: 1\nd: 12.5\nf: "some string"', + ); + + expect(config).toStrictEqual({ + a: true, + b: false, + c: 1, + d: 12.5, + f: "some string", + }); +}); + +test("Two-level configuration", () => { + const config = FlatMatter.config( + 'a.a: true\nb.b: false\nc.c: 1\nd.d: 12.5\nf.f: "some string"', + ); + + expect(config).toStrictEqual({ + a: { + a: true, + }, + b: { + b: false, + }, + c: { + c: 1, + }, + d: { + d: 12.5, + }, + f: { + f: "some string", + }, + }); +}); + +test("Simple function usage", () => { + const toUpper = { + name: "to-upper", + compute: (input: string): unknown => { + return input.toUpperCase(); + }, + }; + + const config = FlatMatter.config('a: (to-upper "value")', [toUpper]); + + expect(config).toStrictEqual({ + a: "VALUE", + }); +}); + +test("Piped function by reference usage", () => { + const toUpper = { + name: "to-upper", + compute: (input: string): unknown => { + return input.toUpperCase(); + }, + }; + + const config = FlatMatter.config('a: "value" / to-upper', [toUpper]); + + expect(config).toStrictEqual({ + a: "VALUE", + }); +}); + +test("Piped function by call usage", () => { + const toUpper = { + name: "to-upper", + compute: (input: string, additional: number): unknown => { + return `${input.toUpperCase()}-${additional}`; + }, + }; + + const config = FlatMatter.config('a: "value" / (to-upper 123)', [toUpper]); + + expect(config).toStrictEqual({ + a: "VALUE-123", + }); +}); + +test("Invalid value in pipe", () => { + const config = FlatMatter.config('a: "value" / / asd'); + + expect(config).toStrictEqual({}); +}); + +test("Invalid value in pipe, 2", () => { + const config = FlatMatter.config('a: "value" / asd'); + + expect(config).toStrictEqual({ + a: "value", + }); +}); + +test("Only piped functions", () => { + const firstFn = { + name: "first-fn", + compute: (input: string): unknown => { + return input.toUpperCase(); + }, + }; + + const secondFn = { + name: "second-fn", + compute: (input: string): unknown => { + return `${input}-passed-by-second`; + }, + }; + + const config = FlatMatter.config('a: (first-fn "value / here") / second-fn', [ + firstFn, + secondFn, + ]); + + expect(config).toStrictEqual({ + a: "VALUE / HERE-passed-by-second", + }); +}); + +test("Function call without any args", () => { + const toUpper = { + name: "to-upper", + compute: (input: string): unknown => { + return input.toUpperCase(); + }, + }; + + const config = FlatMatter.config('a: "value" / (to-upper)', [toUpper]); + + expect(config).toStrictEqual({ + a: "VALUE", + }); +}); + +test("Function call using multiple strings with spaces as arg", () => { + const toUpper = { + name: "to-upper", + compute: (input: string): unknown => { + return input.toUpperCase(); + }, + }; + + const config = FlatMatter.config( + 'a: (to-upper "value goes here" "and here")', + [toUpper], + ); + + expect(config).toStrictEqual({ + a: "VALUE GOES HERE", + }); +}); test("Line has no value separator", () => { assert.throws( - () => new FlatMatter("test"), - "Line on index 0 doesn't have a value separator." + () => FlatMatter.config("test"), + "Line on index 0 doesn't have a value separator.", ); }); test("Line can only have one value separator", () => { assert.throws( - () => new FlatMatter("test: this: that"), - "Line on index 0 has multiple value separators." + () => FlatMatter.config("test: this: that"), + "Line on index 0 has multiple value separators.", ); }); test("String values can have colon characters", () => { - assert.doesNotThrow(() => new FlatMatter('test: "this : that"'), Error); + assert.doesNotThrow(() => FlatMatter.config('test: "this : that"'), Error); }); test("FrontMatter creates a new content entry", () => { - const fm = new FlatMatter( - `---\nthis: true\n---\n\nMarkdown goes here.\n\nAnd here.` + const config = FlatMatter.config( + `---\nthis: true\n---\n\nMarkdown goes here.\n\nAnd here.`, ); - const result = fm.serialize(new ToObject()); - assert.deepEqual(result, { + assert.deepEqual(config, { this: true, content: "Markdown goes here.\n\nAnd here.", }); diff --git a/src/flatmatter.ts b/src/flatmatter.ts index e81df1f..1185020 100644 --- a/src/flatmatter.ts +++ b/src/flatmatter.ts @@ -1,106 +1,63 @@ import { EOL } from "node:os"; +import * as Effect from "effect/Effect"; +import * as Context from "effect/Context"; +import * as Ref from "effect/Ref"; +import * as Cause from "effect/Cause"; +import * as Option from "effect/Option"; +import * as Schema from "effect/Schema"; import { trimChar } from "./utils.ts"; +import ToJson from "./serializers/to_json.ts"; -export type ParsedValue = { - value: unknown; - computeActions: ComputeAction[]; -}; - -export type ComputeAction = { - identifier: string; - args: Array<unknown>; -}; +const ComputeAction = Schema.Struct({ + identifier: Schema.NonEmptyString, + args: Schema.Array(Schema.Unknown), +}); -export interface Serializer<T> { - serialize(config: Record<string, unknown>): T; -} +const ParsedValue = Schema.Struct({ + value: Schema.Unknown, + computeActions: Schema.Array(ComputeAction), +}); -export interface FlatMatterFn { +type Function = { name: string; - compute(...args: unknown[]): unknown; -} - -export default class FlatMatter { - private content: string; - private parsedConfig: Record<string, unknown> = {}; - private functions: FlatMatterFn[]; - - constructor(content: string, functions: FlatMatterFn[] = []) { - this.content = content; - this.functions = functions; - this.parse(); - } - - private parse(): void { - const lines = this.content.split(EOL); - let frontMatterBreakCount = 0; - - for (let i = 0; i < lines.length; i++) { - if (lines[i].trim() === "---" && frontMatterBreakCount < 2) { - frontMatterBreakCount++; - continue; - } - - // FlatMatter ends, Markdown begins - if (frontMatterBreakCount < 2) { - this.parseLine(i, lines[i]); - continue; - } - - this.parsedConfig.content = lines.slice(i).join(EOL).trim(); - break; - } - } - - /** - * Parses a given line of FlatMatter. - */ - private parseLine(idx: number, line: string): void { - this.validateLineConformance(idx, line); - - const keys = line.split(":")[0].trim().split("."); - const value = line.split(":").slice(1).join(":").trim(); - const parsedValue = this.parseValue(value); - - if (!parsedValue) return; - - const config = keys.reduceRight((acc, key) => { - return { [key]: acc }; - }, this.computeValue(parsedValue)) as Record<string, unknown>; - - this.parsedConfig = { ...this.parsedConfig, ...config }; - } - - /** - * For better developer experience, this validates each line - * against some common mistakes you can make, and throws an Error - * if you did. - */ - private validateLineConformance(idx: number, line: string): void { - const validators = [ - this.validateLineHasKeyVal, - this.validateLineHasOnlyOneColonChar, - ]; - - for (const validator of validators) { - validator(idx, line); - } - } +}; - /** - * Validates that the given line has a value separator. - */ - private validateLineHasKeyVal(idx: number, line: string): void { +class FunctionsState extends Context.Tag("FunctionsState")< + FunctionsState, + Ref.Ref<Function[]> +>() {} + +/** + * State for holding the content string. + */ +class ContentState extends Context.Tag("ContentState")< + ContentState, + Ref.Ref<string> +>() {} + +/** + * State for holding the parsed configuration. + */ +class ConfigState extends Context.Tag("ConfigState")< + ConfigState, + Ref.Ref<Record<string, unknown>> +>() {} + +const validateLineHasKeyValEffect = (idx: number, line: string) => + Effect.gen(function* () { if (!line.includes(":")) { - throw new Error(`Line on index ${idx} doesn't have a value separator.`); + yield* Effect.fail( + Cause.fail(`Line on index ${idx} doesn't have a value separator.`), + ); } - } + }); - /** - * Validates that the given line has only one value separator. - */ - private validateLineHasOnlyOneColonChar(idx: number, line: string): void { +/** + * Validates that the given line has only one value separator. + */ +const validateLineHasOnlyOneColonCharEffect = (idx: number, line: string) => + Effect.gen(function* () { let separatorCount = 0; let parts = line.split(":").slice(1); @@ -114,242 +71,283 @@ export default class FlatMatter { } if (separatorCount > 1) { - throw new Error(`Line on index ${idx} has multiple value separators.`); + yield* Effect.fail( + Cause.fail(`Line on index ${idx} has multiple value separators.`), + ); } - } + }); + +const validateLineConformanceEffect = (idx: number, line: string) => + Effect.gen(function* () { + const validatorEffects = [ + validateLineHasKeyValEffect, + validateLineHasOnlyOneColonCharEffect, + ]; + + for (const validatorEffect of validatorEffects) { + yield* validatorEffect(idx, line); + } + }); - /** - * Detects if the value is a simple value. A simple value is any - * of the following: `"a string"`, boolean `true` or `false`, or - * anything numeric like `12345` or `123.45`. - */ - private isSimpleValue(value: string): boolean { - const isString = value.startsWith('"') && value.endsWith('"'); - const isBoolean = value === "true" || value === "false"; - const isNumber = !isNaN(parseFloat(value)); - - return isString || isBoolean || isNumber; +const isSimpleValue = (value: string): boolean => { + const isString = value.startsWith('"') && value.endsWith('"'); + const isBoolean = value === "true" || value === "false"; + const isNumber = !isNaN(parseFloat(value)); + + return isString || isBoolean || isNumber; +}; + +const parseSimpleValue = (value: string): string | number | boolean => { + if (value === "true" || value === "false") { + return value === "true"; } - /** - * Detects if the value is a function value. A function value is any - * of the following: - * - * - A function call with arguments: `(function-name *args)` - * - A function call by reference: `function-name` - */ - private isFunctionValue(value: string): boolean { - const isFnCall = value.startsWith("(") && value.endsWith(")"); - const isFnReference = !!value.match(/^([a-zA-Z0-9_-]+)$/); - - return isFnCall || isFnReference; + if (!Number.isNaN(parseInt(value)) && value.indexOf(".") === -1) { + return parseInt(value); } - /** - * Detects if the value is a piped value. A piped value is a mix of - * simple and function value parts, piped together with the forward - * slash `/` character. For example: - * - * ```yaml - * posts: (get-content "posts") / (limit 10) / only-published - * ``` - * - * or: - * - * ```yaml - * posts: "posts" / get-content / (limit 10) / only-published - * ``` - * - * The result of the previous pipe gets passed to the next as a first - * argument. - */ - private isPipedValue(value: string): boolean { - return this.composePipedValueParts(value).every((part) => { - return this.isSimpleValue(part) || this.isFunctionValue(part); - }); + if (!Number.isNaN(parseFloat(value)) && value.indexOf(".") !== -1) { + return parseFloat(value); } - /** - * Parses a value to a `ParsedValue` object, or `null` - * in case it could not for whatever reason. - */ - private parseValue(value: string): ParsedValue | null { - if (this.isSimpleValue(value)) { - return { - value: this.parseSimpleValue(value), - computeActions: [], - }; - } + return trimChar(value, '"'); +}; - if (this.isFunctionValue(value)) { - return { - value: null, - computeActions: [this.parseFunctionValue(value)], - }; - } +const isFunctionValue = (value: string): boolean => { + const isFnCall = value.startsWith("(") && value.endsWith(")"); + const isFnReference = !!value.match(/^([a-zA-Z0-9_-]+)$/); - if (this.isPipedValue(value)) { - return this.parsePipedValue(value); - } + return isFnCall || isFnReference; +}; - return null; +const parseFunctionValueArgs = (value: string): unknown[] => { + const parts = value + .substring(1, value.length - 1) + .split(" ") + .slice(1); + + if (!parts.length) { + return []; } - /** - * Parses the value part of a line into a simple value, like for example - * a `string`, `number` or `boolean`. - */ - private parseSimpleValue(value: string): string | number | boolean { - if (value === "true" || value === "false") { - return value === "true"; - } + const normalizedParts = [parts[0]]; - if (!Number.isNaN(parseInt(value)) && value.indexOf(".") === -1) { - return parseInt(value); - } + for (let i = 1; i < parts.length; i++) { + const untilCurrent = normalizedParts.join(" "); + const quoteCount = untilCurrent.split('"').length - 1; - if (!Number.isNaN(parseFloat(value)) && value.indexOf(".") !== -1) { - return parseFloat(value); + if (quoteCount % 2 === 0) { + normalizedParts.push(parts[i]); + continue; } - return trimChar(value, '"'); + const lastIndex = normalizedParts.length - 1; + const lastPart = normalizedParts[lastIndex]; + + normalizedParts[lastIndex] = `${lastPart} ${parts[i]}`; } - /** - * Parses the value part of a line into a Compute Action, which is - * later executed to run the function described in FlatMatter. - */ - private parseFunctionValue(value: string): ComputeAction { - const isFn = value.startsWith("(") && value.endsWith(")"); - - if (!isFn) { - return { - identifier: value, - args: [], - }; - } + return normalizedParts.map((part) => parseSimpleValue(part)); +}; - const fnName = trimChar(value, ["(", ")"]).split(" ")[0].trim(); - const fnArgs = this.parseFunctionValueArgs(value); +const parseFunctionValue = ( + value: string, +): Schema.Schema.Type<typeof ComputeAction> => { + const isFn = value.startsWith("(") && value.endsWith(")"); - return { - identifier: fnName, - args: fnArgs, - }; + if (!isFn) { + return ComputeAction.make({ + identifier: value, + args: [], + }); } - /** - * Parses the value part of a line into a ParsedValue, which is - * composed out of piped parts separated by the forward slash `/` character. - * - * The ParsedValue will include the default value, if any, and a list of compute - * actions which will later be executed. - */ - private parsePipedValue(value: string): ParsedValue { - const parts = this.composePipedValueParts(value); - - if (this.isSimpleValue(parts[0])) { - return { - value: this.parseSimpleValue(parts[0]), - computeActions: parts.slice(1).map((p) => this.parseFunctionValue(p)), - }; - } + const fnName = trimChar(value, ["(", ")"]).split(" ")[0].trim(); + const fnArgs = parseFunctionValueArgs(value); - return { - value: null, - computeActions: parts.map((p) => this.parseFunctionValue(p)), - }; - } + return ComputeAction.make({ + identifier: fnName, + args: fnArgs, + }); +}; + +const composePipedValueParts = (value: string): string[] => { + const parts = value.split(" / "); + const normalizedParts = [parts[0]]; + + for (let i = 1; i < parts.length; i++) { + const untilCurrent = normalizedParts.join(" / "); + const quoteCount = untilCurrent.split('"').length - 1; - /** - * Takes the entire value part of a line and, assuming it is a function value, - * parses it into a list of arguments to be passed down to the function. - */ - private parseFunctionValueArgs(value: string): unknown[] { - const parts = value - .substring(1, value.length - 1) - .split(" ") - .slice(1); - - if (!parts.length) { - return []; + if (quoteCount % 2 === 0) { + normalizedParts.push(parts[i]); + continue; } - const normalizedParts = [parts[0]]; + const lastIndex = normalizedParts.length - 1; + const lastPart = normalizedParts[lastIndex]; - for (let i = 1; i < parts.length; i++) { - const untilCurrent = normalizedParts.join(" "); - const quoteCount = untilCurrent.split('"').length - 1; + normalizedParts[lastIndex] = `${lastPart} / ${parts[i]}`; + } - if (quoteCount % 2 === 0) { - normalizedParts.push(parts[i]); - continue; - } + return normalizedParts; +}; - const lastIndex = normalizedParts.length - 1; - const lastPart = normalizedParts[lastIndex]; +const isPipedValue = (value: string): boolean => { + return composePipedValueParts(value).every((part) => { + return isSimpleValue(part) || isFunctionValue(part); + }); +}; - normalizedParts[lastIndex] = `${lastPart} ${parts[i]}`; - } +const parsePipedValue = (value: string): typeof ParsedValue.Type => { + const parts = composePipedValueParts(value); - return normalizedParts.map((part) => this.parseSimpleValue(part)); + if (isSimpleValue(parts[0])) { + return ParsedValue.make({ + value: parseSimpleValue(parts[0]), + computeActions: parts.slice(1).map((p) => parseFunctionValue(p)), + }); } - /** - * Takes an entire value of a line and composes it into a list - * of piped parts. - */ - private composePipedValueParts(value: string): string[] { - const parts = value.split(" / "); - const normalizedParts = [parts[0]]; - - for (let i = 1; i < parts.length; i++) { - const untilCurrent = normalizedParts.join(" / "); - const quoteCount = untilCurrent.split('"').length - 1; + return ParsedValue.make({ + value: null, + computeActions: parts.map((p) => parseFunctionValue(p)), + }); +}; - if (quoteCount % 2 === 0) { - normalizedParts.push(parts[i]); - continue; - } +const parseValueEffect = (value: string) => + Effect.gen(function* () { + if (isSimpleValue(value)) { + return Option.some( + ParsedValue.make({ + value: parseSimpleValue(value), + computeActions: [], + }), + ); + } - const lastIndex = normalizedParts.length - 1; - const lastPart = normalizedParts[lastIndex]; + if (isFunctionValue(value)) { + return Option.some( + ParsedValue.make({ + value: null, + computeActions: [parseFunctionValue(value)], + }), + ); + } - normalizedParts[lastIndex] = `${lastPart} / ${parts[i]}`; + if (isPipedValue(value)) { + return Option.some(parsePipedValue(value)); } - return normalizedParts; - } + return Option.none(); + }); - /** - * Takes ParsedValue and, optionally an initial value, and runs - * compute actions over it to return the final computed value. - */ - private computeValue(parsedValue: ParsedValue): unknown { +const computeValueEffect = (parsedValue: typeof ParsedValue.Type) => + Effect.gen(function* () { let value = parsedValue.value; + const functions = yield* Ref.get(yield* FunctionsState); - for (const ca of parsedValue.computeActions) { - const fnInstance = this.functions.find((f) => f.name === ca.identifier); + for (const computeAction of parsedValue.computeActions) { + const fn = functions.find((f) => f.name === computeAction.identifier); - if (!fnInstance) { + if (!fn) { continue; } if (value !== null) { - ca.args = [value, ...ca.args]; + value = fn.compute(value, ...computeAction.args); + continue; } - value = fnInstance.compute(...ca.args); + value = fn.compute(...computeAction.args); } return value; - } + }); + +const parseLineEffect = (idx: number, line: string) => + Effect.gen(function* () { + yield* validateLineConformanceEffect(idx, line); + + const keys = line.split(":")[0].trim().split("."); + const value = line.split(":").slice(1).join(":").trim(); + const parsedValue = yield* parseValueEffect(value); + + if (Option.isNone(parsedValue)) return; + + const updatedConfig = keys.reduceRight( + (acc, key) => { + return { [key]: acc }; + }, + yield* computeValueEffect(parsedValue.value), + ) as Record<string, unknown>; + + yield* Ref.update(yield* ConfigState, (config) => { + return { ...config, ...updatedConfig }; + }); + }); + +const parseContentEffect = Effect.gen(function* () { + const content = yield* Ref.get(yield* ContentState); + const lines = content.split(EOL); + let frontMatterBreakCount = 0; - /** - * Takes a Serializer and uses it to transform internal data - * object to a desired output. - */ - public serialize<T>(serializer: Serializer<T>): T { - return serializer.serialize(this.parsedConfig); + for (let i = 0; i < lines.length; i++) { + if (lines[i].trim() === "---" && frontMatterBreakCount < 2) { + frontMatterBreakCount++; + continue; + } + + if (frontMatterBreakCount < 2) { + yield* parseLineEffect(i, lines[i]); + continue; + } + + // FlatMatter ends, Markdown begins + yield* Ref.update(yield* ConfigState, (config) => { + config.content = lines.slice(i).join(EOL).trim(); + return config; + }); + + break; } -} +}); + +/** + * + */ +const composeConfigEffect = Effect.gen(function* () { + yield* parseContentEffect; + + return yield* Ref.get(yield* ConfigState); +}); + +const config = ( + content: string, + functions: Function[] = [], +): Record<string, unknown> => { + return Effect.runSync( + composeConfigEffect.pipe( + Effect.provideServiceEffect(ContentState, Ref.make(content)), + Effect.provideServiceEffect(ConfigState, Ref.make({})), + Effect.provideServiceEffect(FunctionsState, Ref.make(functions)), + ), + ); +}; + +export type Serializer<T> = (config: Record<string, unknown>) => T; + +const serialize = <T,>( + config: Record<string, unknown>, + serializer: Serializer<T>, +): T => { + return serializer(config); +}; + +export default { + config, + serialize, + Serializers: { + ToJson, + }, +}; diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 7ac5415..0000000 --- a/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import FlatMatter, {FlatMatterFn, Serializer} from "./flatmatter.ts"; -import ToObject from "./serializers/to_object.ts"; -import ToJson from "./serializers/to_json.ts"; - -export { - FlatMatter, - FlatMatterFn, - Serializer, - ToObject, - ToJson, -}
\ No newline at end of file diff --git a/src/serializers/to_json.test.ts b/src/serializers/to_json.test.ts index 4184ab1..78f704a 100644 --- a/src/serializers/to_json.test.ts +++ b/src/serializers/to_json.test.ts @@ -2,11 +2,11 @@ import FlatMatter from "../flatmatter.ts"; import ToJson from "./to_json.ts"; test("Single-level configuration", () => { - const fm = new FlatMatter( - 'a: true\nb: false\nc: 1\nd: 12.5\nf: "some string"' + const config = FlatMatter.config( + 'a: true\nb: false\nc: 1\nd: 12.5\nf: "some string"', ); const equal = '{"a":true,"b":false,"c":1,"d":12.5,"f":"some string"}'; - expect(fm.serialize(new ToJson())).toStrictEqual(equal); + expect(FlatMatter.serialize(config, ToJson)).toStrictEqual(equal); }); diff --git a/src/serializers/to_json.ts b/src/serializers/to_json.ts index 30cf417..c61a98f 100644 --- a/src/serializers/to_json.ts +++ b/src/serializers/to_json.ts @@ -1,7 +1,9 @@ import type { Serializer } from "../flatmatter.ts"; -export default class ToJson implements Serializer<string> { - serialize(config: Record<string, unknown>): string { - return JSON.stringify(config); - } -} +const ToJson: Serializer<string> = ( + config: Record<string, unknown>, +): string => { + return JSON.stringify(config); +}; + +export default ToJson; diff --git a/src/serializers/to_object.test.ts b/src/serializers/to_object.test.ts deleted file mode 100644 index 28cb78e..0000000 --- a/src/serializers/to_object.test.ts +++ /dev/null @@ -1,171 +0,0 @@ -import FlatMatter, { type FlatMatterFn } from "../flatmatter.ts"; -import ToObject from "./to_object.ts"; - -test("Single-level configuration", () => { - const fm = new FlatMatter( - 'a: true\nb: false\nc: 1\nd: 12.5\nf: "some string"' - ); - - expect(fm.serialize(new ToObject())).toStrictEqual({ - a: true, - b: false, - c: 1, - d: 12.5, - f: "some string", - }); -}); - -test("Two-level configuration", () => { - const fm = new FlatMatter( - 'a.a: true\nb.b: false\nc.c: 1\nd.d: 12.5\nf.f: "some string"' - ); - - expect(fm.serialize(new ToObject())).toStrictEqual({ - a: { - a: true, - }, - b: { - b: false, - }, - c: { - c: 1, - }, - d: { - d: 12.5, - }, - f: { - f: "some string", - }, - }); -}); - -test("Simple function usage", () => { - class ToUpper implements FlatMatterFn { - name = "to-upper"; - - compute(input: string): unknown { - return input.toUpperCase(); - } - } - - const fm = new FlatMatter('a: (to-upper "value")', [new ToUpper()]); - const config = fm.serialize(new ToObject()); - - expect(config).toStrictEqual({ - a: "VALUE", - }); -}); - -test("Piped function by reference usage", () => { - class ToUpper implements FlatMatterFn { - name = "to-upper"; - - compute(input: string): unknown { - return input.toUpperCase(); - } - } - - const fm = new FlatMatter('a: "value" / to-upper', [new ToUpper()]); - const config = fm.serialize(new ToObject()); - - expect(config).toStrictEqual({ - a: "VALUE", - }); -}); - -test("Piped function by call usage", () => { - class ToUpper implements FlatMatterFn { - name = "to-upper"; - - compute(input: string, additional: number): unknown { - return `${input.toUpperCase()}-${additional}`; - } - } - - const fm = new FlatMatter('a: "value" / (to-upper 123)', [new ToUpper()]); - const config = fm.serialize(new ToObject()); - - expect(config).toStrictEqual({ - a: "VALUE-123", - }); -}); - -test("Invalid value in pipe", () => { - const fm = new FlatMatter('a: "value" / / asd'); - const config = fm.serialize(new ToObject()); - - expect(config).toStrictEqual({}); -}); - -test("Invalid value in pipe, 2", () => { - const fm = new FlatMatter('a: "value" / asd'); - const config = fm.serialize(new ToObject()); - - expect(config).toStrictEqual({ - a: "value", - }); -}); - -test("Only piped functions", () => { - class FirstFn implements FlatMatterFn { - name = "first-fn"; - - compute(input: string): unknown { - return input.toUpperCase(); - } - } - - class SecondFn implements FlatMatterFn { - name = "second-fn"; - - compute(input: string): unknown { - return `${input}-passed-by-second`; - } - } - - const fm = new FlatMatter('a: (first-fn "value / here") / second-fn', [ - new FirstFn(), - new SecondFn(), - ]); - const config = fm.serialize(new ToObject()); - - expect(config).toStrictEqual({ - a: "VALUE / HERE-passed-by-second", - }); -}); - -test("Function call without any args", () => { - class ToUpper implements FlatMatterFn { - name = "to-upper"; - - compute(input: string): unknown { - return input.toUpperCase(); - } - } - - const fm = new FlatMatter('a: "value" / (to-upper)', [new ToUpper()]); - const config = fm.serialize(new ToObject()); - - expect(config).toStrictEqual({ - a: "VALUE", - }); -}); - -test("Function call using multiple strings with spaces as arg", () => { - class ToUpper implements FlatMatterFn { - name = "to-upper"; - - compute(input: string): unknown { - return input.toUpperCase(); - } - } - - const fm = new FlatMatter('a: (to-upper "value goes here" "and here")', [ - new ToUpper(), - ]); - const config = fm.serialize(new ToObject()); - - expect(config).toStrictEqual({ - a: "VALUE GOES HERE", - }); -}); diff --git a/src/serializers/to_object.ts b/src/serializers/to_object.ts deleted file mode 100644 index 79b2886..0000000 --- a/src/serializers/to_object.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Serializer } from "../flatmatter.ts"; - -export default class ToObject implements Serializer<Record<string, unknown>> { - serialize(config: Record<string, unknown>): Record<string, unknown> { - return config; - } -} diff --git a/tsconfig.json b/tsconfig.json index 21eeee6..60e3198 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,7 @@ "outDir": "dist", "strict": true, "esModuleInterop": true, + "exactOptionalPropertyTypes": true, "lib": ["esnext"], "types": ["vitest/globals"] } diff --git a/tsup.config.ts b/tsup.config.ts index 76a9a74..ca2b9c0 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,8 +1,10 @@ -import {defineConfig} from "tsup"; +import { defineConfig } from "tsup"; export default defineConfig({ - entry: ["src/index.ts"], - clean: true, - format: ["cjs", "esm"], - dts: true -})
\ No newline at end of file + entry: ["src/flatmatter.ts"], + clean: true, + format: ["cjs", "esm"], + dts: true, + treeshake: "smallest", + sourcemap: true, +}); |
