diff --git a/package-lock.json b/package-lock.json index 13f91c711..45fd8afc0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -579,16 +579,6 @@ "node": ">= 6" } }, - "node_modules/@cypress/request/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", @@ -1444,6 +1434,49 @@ "node": ">=12" } }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1539,13 +1572,6 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "license": "ISC", - "optional": true - }, "node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -1599,22 +1625,6 @@ "semver": "bin/semver.js" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4109,14 +4119,11 @@ } }, "node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "devOptional": true, + "license": "ISC" }, "node_modules/abort-controller": { "version": "3.0.0", @@ -4870,13 +4877,6 @@ "pako": "~0.2.0" } }, - "node_modules/browserify-zlib/node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", - "dev": true, - "license": "MIT" - }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -5217,41 +5217,19 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" } }, "node_modules/chownr": { @@ -5382,23 +5360,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/cli-highlight/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cli-highlight/node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, "node_modules/cli-highlight/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -5444,19 +5405,6 @@ "node": ">=8" } }, - "node_modules/cli-highlight/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cli-highlight/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -5530,13 +5478,6 @@ "node": ">=8" } }, - "node_modules/cli-table3/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -5592,23 +5533,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", - "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/clipanion": { "version": "4.0.0-rc.4", "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-4.0.0-rc.4.tgz", @@ -5666,13 +5590,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, "node_modules/cliui/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -5818,13 +5735,13 @@ } }, "node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/comment-parser": { @@ -5974,6 +5891,22 @@ "node": ">=8" } }, + "node_modules/concurrently/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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/confbox": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", @@ -6934,6 +6867,16 @@ "node": ">=14" } }, + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/editorconfig/node_modules/minimatch": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", @@ -6981,10 +6924,10 @@ "license": "MIT" }, "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true, "license": "MIT" }, "node_modules/emojilib": { @@ -7661,19 +7604,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -8514,13 +8444,6 @@ "node": ">=8" } }, - "node_modules/gauge/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "optional": true - }, "node_modules/gauge/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -8726,21 +8649,21 @@ "license": "MIT" }, "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "dev": true, + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -8759,6 +8682,28 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/global-directory": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", @@ -8807,37 +8752,6 @@ "node": ">=8" } }, - "node_modules/globby/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/globby/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/globby/node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -8847,18 +8761,6 @@ "node": ">= 4" } }, - "node_modules/globby/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -8980,16 +8882,6 @@ "uglify-js": "^3.1.4" } }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9327,12 +9219,13 @@ } }, "node_modules/highlight.js": { - "version": "11.11.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", - "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, "license": "BSD-3-Clause", "engines": { - "node": ">=12.0.0" + "node": "*" } }, "node_modules/hmac-drbg": { @@ -10130,19 +10023,6 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-source-maps": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", @@ -10240,6 +10120,63 @@ "node": ">=14" } }, + "node_modules/js-beautify/node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/js-beautify/node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", @@ -10473,6 +10410,12 @@ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT" }, + "node_modules/jszip/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/jszip/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -10625,16 +10568,6 @@ "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, "node_modules/listr2": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", @@ -10653,49 +10586,6 @@ "node": ">=20.0.0" } }, - "node_modules/listr2/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/listr2/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -10937,49 +10827,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-update/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -12143,13 +11990,13 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "devOptional": true, "license": "ISC", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=8" } }, "node_modules/minizlib": { @@ -12260,12 +12107,21 @@ "vue": "^3.0.0" } }, - "node_modules/nan": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", - "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", - "license": "MIT", - "optional": true + "node_modules/naive-ui/node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/nan": { + "version": "2.23.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", + "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "license": "MIT", + "optional": true }, "node_modules/nano-spawn": { "version": "2.0.0", @@ -12467,6 +12323,13 @@ "pako": "~1.0.5" } }, + "node_modules/node-stdlib-browser/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/nodemon": { "version": "3.1.11", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", @@ -12507,6 +12370,44 @@ "concat-map": "0.0.1" } }, + "node_modules/nodemon/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/nodemon/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -12530,6 +12431,19 @@ "node": "*" } }, + "node_modules/nodemon/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/nodemon/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -12544,19 +12458,19 @@ } }, "node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "devOptional": true, "license": "ISC", "dependencies": { - "abbrev": "^2.0.0" + "abbrev": "1" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" } }, "node_modules/normalize-package-data": { @@ -15655,10 +15569,11 @@ "license": "BlueOak-1.0.0" }, "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "dev": true, + "license": "MIT" }, "node_modules/parent-module": { "version": "1.0.1", @@ -17115,16 +17030,17 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/real-require": { @@ -17406,52 +17322,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "devOptional": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/ripemd160": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz", @@ -17669,6 +17539,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/rollup-plugin-visualizer/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, "node_modules/rope-sequence": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", @@ -18566,13 +18446,13 @@ } }, "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", "engines": { - "node": ">= 12" + "node": ">=0.10.0" } }, "node_modules/source-map-js": { @@ -18833,18 +18713,17 @@ } }, "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -18876,13 +18755,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -19062,19 +18934,16 @@ "link": true }, "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==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=8" } }, "node_modules/supports-hyperlinks": { @@ -19094,19 +18963,6 @@ "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" } }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -19204,16 +19060,6 @@ "streamx": "^2.15.0" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "devOptional": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, "node_modules/temp-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", @@ -19284,6 +19130,37 @@ "node": ">=18" } }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/text-decoder": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", @@ -19776,34 +19653,14 @@ } } }, - "node_modules/tsup/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "node_modules/tsup/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/tsup/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "node": ">= 12" } }, "node_modules/tsup/node_modules/tinyexec": { @@ -20766,13 +20623,10 @@ } }, "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, "license": "MIT", "bin": { "uuid": "dist/bin/uuid" @@ -22028,13 +21882,6 @@ "node": ">=8" } }, - "node_modules/wide-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "optional": true - }, "node_modules/wide-align/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -22091,18 +21938,18 @@ "license": "MIT" }, "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -22153,13 +22000,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -22198,6 +22038,31 @@ "node": ">=8" } }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -22397,13 +22262,6 @@ "node": ">=8" } }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -23172,19 +23030,6 @@ "yjs": "13.6.19" } }, - "packages/ai/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "packages/ai/node_modules/ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", @@ -23211,6 +23056,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/ai/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "packages/ai/node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -23244,8 +23103,6 @@ }, "packages/collaboration-yjs/node_modules/@bcoe/v8-coverage": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, "license": "MIT" }, @@ -23319,8 +23176,6 @@ }, "packages/collaboration-yjs/node_modules/@esbuild/darwin-arm64": { "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -23642,8 +23497,6 @@ }, "packages/collaboration-yjs/node_modules/@vitest/coverage-v8": { "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": { @@ -23675,8 +23528,6 @@ }, "packages/collaboration-yjs/node_modules/@vitest/expect": { "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": { @@ -23689,10 +23540,33 @@ "url": "https://opencollective.com/vitest" } }, + "packages/collaboration-yjs/node_modules/@vitest/mocker": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, "packages/collaboration-yjs/node_modules/@vitest/pretty-format": { "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": { @@ -23704,8 +23578,6 @@ }, "packages/collaboration-yjs/node_modules/@vitest/runner": { "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", - "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", "dev": true, "license": "MIT", "dependencies": { @@ -23718,8 +23590,6 @@ }, "packages/collaboration-yjs/node_modules/@vitest/snapshot": { "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", - "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -23733,8 +23603,6 @@ }, "packages/collaboration-yjs/node_modules/@vitest/spy": { "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": { @@ -23746,8 +23614,6 @@ }, "packages/collaboration-yjs/node_modules/@vitest/utils": { "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": { @@ -23761,8 +23627,6 @@ }, "packages/collaboration-yjs/node_modules/esbuild": { "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -23800,15 +23664,11 @@ }, "packages/collaboration-yjs/node_modules/pathe": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", "dev": true, "license": "MIT" }, "packages/collaboration-yjs/node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.5.3", "dev": true, "license": "MIT", "bin": { @@ -23823,15 +23683,11 @@ }, "packages/collaboration-yjs/node_modules/tinyexec": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "dev": true, "license": "MIT" }, "packages/collaboration-yjs/node_modules/tinyrainbow": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", "dev": true, "license": "MIT", "engines": { @@ -23840,41 +23696,14 @@ }, "packages/collaboration-yjs/node_modules/tinyspy": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, - "packages/collaboration-yjs/node_modules/vite-node": { - "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": { - "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "packages/collaboration-yjs/node_modules/vite-node/node_modules/vite": { + "packages/collaboration-yjs/node_modules/vite": { "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", "dependencies": { @@ -23931,10 +23760,29 @@ } } }, + "packages/collaboration-yjs/node_modules/vite-node": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "packages/collaboration-yjs/node_modules/vitest": { "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", - "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -23997,93 +23845,6 @@ } } }, - "packages/collaboration-yjs/node_modules/vitest/node_modules/@vitest/mocker": { - "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.9", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "packages/collaboration-yjs/node_modules/vitest/node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, "packages/layout-engine": { "name": "@superdoc/layout-engine-workspace" }, @@ -25282,9 +25043,7 @@ } }, "packages/super-editor/node_modules/marked": { - "version": "16.4.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", - "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "version": "16.3.0", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -25293,6 +25052,17 @@ "node": ">= 20" } }, + "packages/super-editor/node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "packages/superdoc": { "version": "0.31.4-RC.1", "license": "AGPL-3.0", @@ -25330,8 +25100,6 @@ }, "packages/superdoc/node_modules/pdfjs-dist": { "version": "4.3.136", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.3.136.tgz", - "integrity": "sha512-gzfnt1qc4yA+U46golPGYtU4WM2ssqP2MvFjKga8GEKOrEnzRPrA/9jogLLPYHiA3sGBPJ+p7BdAq+ytmw3jEg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -25342,6 +25110,17 @@ "path2d": "^0.2.0" } }, + "packages/superdoc/node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "packages/word-layout": { "name": "@superdoc/word-layout", "version": "0.1.0" diff --git a/packages/ai/README.md b/packages/ai/README.md index 51805b8e0..221992bbe 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -4,14 +4,12 @@ ## Features -- 🤖 **Multiple AI Providers**: Built-in support for OpenAI, Anthropic Claude, and custom HTTP endpoints -- 🔍 **Smart Content Finding**: Natural language search across documents -- ✍️ **Intelligent Editing**: AI-powered content replacement, suggestions, and generation -- 💬 **Comment Integration**: Automatically insert AI-generated comments -- 📝 **Track Changes**: AI suggestions with full revision history -- 🎨 **Content Highlighting**: Smart text highlighting based on queries -- 🌊 **Streaming Support**: Real-time AI responses with streaming -- 📦 **TypeScript First**: Full type safety and excellent IDE support +- **AI Actions**: High-level API for common document operations (find, replace, insert, comment) +- **AI Builder**: Low-level primitives for custom AI workflows +- **Anthropic Support**: Built-in Anthropic Claude integration +- **Comment Integration**: Automatically insert AI-generated comments +- **Track Changes**: AI suggestions with full revision history +- **TypeScript First**: Full type safety and excellent IDE support ## Installation @@ -21,19 +19,21 @@ npm install @superdoc-dev/ai ## Quick Start +### Using AI Actions (High-Level API) + ```typescript import { AIActions } from '@superdoc-dev/ai'; -// Initialize with OpenAI +// Initialize with Anthropic const ai = new AIActions(superdoc, { user: { displayName: 'AI Assistant', userId: 'ai-bot-001', }, provider: { - type: 'openai', - apiKey: process.env.OPENAI_API_KEY, - model: 'gpt-4', + type: 'anthropic', + apiKey: process.env.ANTHROPIC_API_KEY, + model: 'claude-sonnet-4-5', }, onReady: ({ aiActions }) => { console.log('AI is ready!'); @@ -56,194 +56,58 @@ await ai.action.insertTrackedChange('improve the introduction'); await ai.action.insertContent('write a conclusion paragraph'); ``` -## API Reference - -### AIActions Class - -The main class for AI integration. - -#### Constructor - -```typescript -new AIActions(superdoc: SuperDocInstance, options: AIActionsOptions) -``` - -**Options:** - -- `user` (required): User/bot information - - `displayName`: Display name for AI-generated changes - - `userId?`: Optional user identifier - - `profileUrl?`: Optional profile image URL -- `provider` (required): AI provider configuration or instance -- `systemPrompt?`: Custom system prompt for AI context -- `enableLogging?`: Enable debug logging (default: false) -- Callbacks: - - `onReady?`: Called when AI is initialized - - `onStreamingStart?`: Called when streaming begins - - `onStreamingPartialResult?`: Called for each streaming chunk - - `onStreamingEnd?`: Called when streaming completes - - `onError?`: Called when an error occurs - -#### Methods - -##### `waitUntilReady()` - -Waits for AI initialization to complete. +### Using AI Builder (Low-Level API) ```typescript -await ai.waitUntilReady(); -``` - -##### `getIsReady()` +import { executeTool, anthropicTools } from '@superdoc-dev/ai'; +import Anthropic from '@anthropic-ai/sdk'; -Checks if AI is ready. +// Get tool definitions +const tools = anthropicTools(editor.extensionManager.extensions); -```typescript -const ready = ai.getIsReady(); // boolean -``` - -##### `getCompletion(prompt, options?)` - -Get a complete AI response. - -```typescript -const response = await ai.getCompletion('Summarize this document', { - temperature: 0.7, - maxTokens: 500, +// Use with Anthropic SDK +const anthropic = new Anthropic({ apiKey: '...' }); +const response = await anthropic.beta.messages.create({ + model: 'claude-sonnet-4-5', + betas: ['structured-outputs-2025-11-13'], + tools, + messages: [{ role: 'user', content: 'Add a paragraph saying hello' }], }); -``` -##### `streamCompletion(prompt, options?)` - -Stream AI responses in real-time. - -```typescript -const result = await ai.streamCompletion('Generate introduction'); +// Execute tool calls +for (const toolUse of response.content.filter((c) => c.type === 'tool_use')) { + await executeTool(toolUse.name, toolUse.input, editor); +} ``` -##### `getDocumentContext()` - -Get current document text. - -```typescript -const context = ai.getDocumentContext(); -``` +## API Overview ### AI Actions -All actions are available via `ai.action.*`. - -#### `find(query)` - -Find the first occurrence of content matching the query. - -```typescript -const result = await ai.action.find('GDPR compliance section'); -// Returns: { success: boolean, results: FoundMatch[] } -``` - -#### `findAll(query)` - -Find all occurrences of content matching the query. - -```typescript -const result = await ai.action.findAll('privacy policy'); -``` - -#### `highlight(query, color?)` - -Find and highlight content. - -```typescript -await ai.action.highlight('important terms', '#FFFF00'); -``` - -#### `replace(instruction)` - -Replace the first occurrence based on instruction. - -```typescript -await ai.action.replace('change "data" to "information" in the first paragraph'); -``` - -#### `replaceAll(instruction)` - -Replace all occurrences based on instruction. - -```typescript -await ai.action.replaceAll('update dates to 2025'); -``` - -#### `insertTrackedChange(instruction)` - -Insert a single tracked change. - -```typescript -await ai.action.insertTrackedChange('improve clarity of terms and conditions'); -``` - -#### `insertTrackedChanges(instruction)` - -Insert multiple tracked changes. - -```typescript -await ai.action.insertTrackedChanges('fix all grammatical errors'); -``` - -#### `insertComment(instruction)` - -Insert a single comment. - -```typescript -await ai.action.insertComment('suggest improvements to introduction'); -``` - -#### `insertComments(instruction)` - -Insert multiple comments. - -```typescript -await ai.action.insertComments('review all legal terms'); -``` - -#### `summarize(instruction)` - -Generate a summary. +High-level API for common document operations: -```typescript -const result = await ai.action.summarize('create executive summary'); -// onStreamingPartialResult receives partial updates when the provider allows streaming. -``` +- `find(query)` - Find content matching a query +- `findAll(query)` - Find all occurrences +- `highlight(query, color?)` - Find and highlight +- `replace(instruction)` - Replace first occurrence +- `replaceAll(instruction)` - Replace all occurrences +- `insertTrackedChange(instruction)` - Insert single tracked change +- `insertTrackedChanges(instruction)` - Insert multiple tracked changes +- `insertComment(instruction)` - Insert single comment +- `insertComments(instruction)` - Insert multiple comments +- `summarize(instruction)` - Generate summary +- `insertContent(instruction)` - Generate and insert content -#### `insertContent(instruction)` +### AI Builder -Generate and insert new content. +Low-level primitives for custom workflows: -```typescript -await ai.action.insertContent('write a conclusion paragraph'); -``` +- `anthropicTools(extensions, options?)` - Generate Anthropic tool definitions +- `executeTool(toolName, params, editor)` - Execute a tool call +- `generateContentSchema(extensions, options?)` - Generate content schema +- Core types and utilities -When the provider configuration leaves `streamResults` enabled (default), generated content streams into the document incrementally instead of waiting for the full response. - -## AI Providers - -### OpenAI - -```typescript -const ai = new AIActions(superdoc, { - user: { displayName: 'AI' }, - provider: { - type: 'openai', - apiKey: 'sk-...', - model: 'gpt-4', - baseURL: 'https://api.openai.com/v1', // optional - organizationId: 'org-...', // optional - temperature: 0.7, // optional - maxTokens: 2000, // optional - streamResults: false, // optional (applies to AI insert/summarize actions; default true) - }, -}); -``` +## Provider Configuration ### Anthropic Claude @@ -253,76 +117,32 @@ const ai = new AIActions(superdoc, { provider: { type: 'anthropic', apiKey: 'sk-ant-...', - model: 'claude-3-opus-20240229', - apiVersion: '2023-06-01', // optional - baseURL: 'https://api.anthropic.com', // optional + model: 'claude-sonnet-4-5', temperature: 0.7, // optional maxTokens: 2000, // optional - streamResults: false, // optional (applies to AI insert/summarize actions; default true) - }, -}); -``` - -### Custom HTTP Provider - -```typescript -const ai = new AIActions(superdoc, { - user: { displayName: 'AI' }, - provider: { - type: 'http', - url: 'https://your-ai-api.com/complete', - streamUrl: 'https://your-ai-api.com/stream', // optional - headers: { - Authorization: 'Bearer token', - 'X-Custom-Header': 'value', - }, - method: 'POST', // default - streamResults: true, // optional (used by insertContent/summarize; default true) - buildRequestBody: (context) => ({ - messages: context.messages, - stream: context.stream, - // custom fields - }), - parseCompletion: (payload) => { - // Extract text from response - return payload.result; - }, }, }); ``` -### Custom Provider Instance +## Advanced Usage -Implement the `AIProvider` interface: +### Custom System Prompt ```typescript -const customProvider: AIProvider = { - streamResults: true, - async *streamCompletion(messages, options) { - // Yield chunks - yield 'chunk1'; - yield 'chunk2'; - }, - async getCompletion(messages, options) { - // Return complete response - return 'response'; - }, -}; - const ai = new AIActions(superdoc, { - user: { displayName: 'AI' }, - provider: customProvider, + user: { displayName: 'Legal AI' }, + provider: { type: 'anthropic', apiKey: '...', model: 'claude-sonnet-4-5' }, + systemPrompt: `You are a legal document assistant. + Focus on accuracy, clarity, and compliance.`, }); ``` -## Advanced Usage - ### With Callbacks ```typescript const ai = new AIActions(superdoc, { user: { displayName: 'AI' }, - provider: { type: 'openai', apiKey: '...', model: 'gpt-4' }, + provider: { type: 'anthropic', apiKey: '...', model: 'claude-sonnet-4-5' }, enableLogging: true, onReady: () => console.log('Ready!'), onStreamingStart: () => console.log('Streaming started'), @@ -338,48 +158,6 @@ const ai = new AIActions(superdoc, { }); ``` -### Custom System Prompt - -```typescript -const ai = new AIActions(superdoc, { - user: { displayName: 'Legal AI' }, - provider: { type: 'openai', apiKey: '...', model: 'gpt-4' }, - systemPrompt: `You are a legal document assistant. - Focus on accuracy, clarity, and compliance. - Always cite relevant regulations when applicable.`, -}); -``` - -### Abort Streaming - -```typescript -const controller = new AbortController(); - -ai.streamCompletion('Long task', { - signal: controller.signal, -}); - -// Later... -controller.abort(); -``` - -### Provider-Specific Options - -```typescript -await ai.getCompletion('prompt', { - temperature: 0.5, - maxTokens: 1000, - stop: ['\n\n'], - providerOptions: { - // OpenAI specific - top_p: 0.9, - frequency_penalty: 0.5, - // or Anthropic specific - top_k: 40, - }, -}); -``` - ## Error Handling ```typescript @@ -395,12 +173,6 @@ try { } ``` -## Testing - -```bash -npm test -``` - ## License AGPL-3.0 - see [LICENSE](../../LICENSE) for details. @@ -410,7 +182,6 @@ AGPL-3.0 - see [LICENSE](../../LICENSE) for details. - 📖 [Documentation](https://superdoc.dev/docs/ai) - 💬 [Discord Community](https://discord.gg/superdoc) - 🐛 [Issue Tracker](https://github.com/harbour-enterprises/superdoc/issues) -- 📧 [Email Support](mailto:support@superdoc.dev) ## Changelog diff --git a/packages/ai/package.json b/packages/ai/package.json index 62a7ca10f..1ae7780b5 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -2,8 +2,15 @@ "name": "@superdoc-dev/ai", "version": "0.1.5", "description": "AI integration package for SuperDoc", + "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, "scripts": { "build": "tsup", "dev": "tsup --watch", @@ -14,11 +21,15 @@ "keywords": [ "superdoc", "ai", + "ai-builder", + "ai-actions", "document", "collaboration", "llm", "openai", - "anthropic" + "anthropic", + "tools", + "structured-outputs" ], "license": "AGPL-3.0", "peerDependencies": { diff --git a/packages/ai/src/ai-builder/README.md b/packages/ai/src/ai-builder/README.md new file mode 100644 index 000000000..ae5a5d1e7 --- /dev/null +++ b/packages/ai/src/ai-builder/README.md @@ -0,0 +1,193 @@ +# SuperDoc AI Builder + +Low-level primitives for building custom AI workflows with SuperDoc. + +## Overview + +AI Builder provides the foundational components for creating AI-powered document editing experiences. Unlike AI Actions which offers pre-built operations, AI Builder gives you the tools to build custom workflows tailored to your specific needs. + +**Alpha Status:** Currently supports Anthropic Claude only. + +## Architecture + +``` +ai-builder/ +├── types.ts # Core type definitions +├── executor.ts # Tool execution primitive (executeTool) +├── tools/ # Core tool implementations +│ ├── insertContent.ts +│ └── replaceContent.ts +├── providers/ # Provider-specific tool schemas +│ └── anthropic.ts # Anthropic Claude support +└── schema-generator/ # Schema generation from extensions + └── from-extensions.ts +``` + +## Quick Start + +```typescript +import { executeTool, anthropicTools } from '@superdoc-dev/ai'; +import Anthropic from '@anthropic-ai/sdk'; + +// Get tool definitions +const tools = anthropicTools(editor.extensionManager.extensions); + +// Use with Anthropic SDK +const anthropic = new Anthropic({ apiKey: '...' }); +const response = await anthropic.beta.messages.create({ + model: 'claude-sonnet-4-5', + betas: ['structured-outputs-2025-11-13'], + tools, + messages: [{ role: 'user', content: 'Add a paragraph saying hello' }], +}); + +// Execute tool calls +for (const toolUse of response.content.filter((c) => c.type === 'tool_use')) { + await executeTool(toolUse.name, toolUse.input, editor); +} +``` + +## Core Concepts + +### Tools + +Tools are the basic operations that AI can perform on documents: + +- **insertContent** - Insert content at selection, documentStart, or documentEnd +- **replaceContent** - Replace content in a specific range + +Each tool executes with type-safe parameters and returns a structured result. + +### Tool Execution + +```typescript +import { executeTool } from '@superdoc-dev/ai'; + +// Execute a single tool +const result = await executeTool( + 'insertContent', + { + position: 'selection', + content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Hello' }] }], + }, + editor, +); + +if (result.success) { + console.log('Content inserted successfully'); +} +``` + +### Schema Generation + +Generate tool definitions compatible with Anthropic Claude: + +```typescript +import { anthropicTools } from '@superdoc-dev/ai'; + +// Get tool definitions from extensions +const tools = anthropicTools(editor.extensionManager.extensions, { + excludedNodes: ['bulletList', 'orderedList'], + excludedMarks: [], + strict: true, +}); +``` + +## Available Tools + +### insertContent + +Insert content at a specific position. + +**Parameters:** + +- `position`: 'selection' | 'documentStart' | 'documentEnd' +- `content`: Array of ProseMirror nodes + +```typescript +await executeTool( + 'insertContent', + { + position: 'selection', + content: [ + { + type: 'paragraph', + content: [{ type: 'text', text: 'Hello World' }], + }, + ], + }, + editor, +); +``` + +### replaceContent + +Replace content in a specific range. + +**Parameters:** + +- `from`: Start position (number) +- `to`: End position (number) +- `content`: Array of ProseMirror nodes + +```typescript +await executeTool( + 'replaceContent', + { + from: 0, + to: 100, + content: [ + { + type: 'paragraph', + content: [{ type: 'text', text: 'New content' }], + }, + ], + }, + editor, +); +``` + +## Anthropic Integration + +AI Builder is optimized for Anthropic Claude with structured outputs: + +```typescript +import { anthropicTools, executeTool } from '@superdoc-dev/ai'; +import Anthropic from '@anthropic-ai/sdk'; + +const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }); +const tools = anthropicTools(editor.extensionManager.extensions); + +async function processUserRequest(userMessage: string) { + const response = await anthropic.beta.messages.create({ + model: 'claude-sonnet-4-5', + betas: ['structured-outputs-2025-11-13'], + tools, + messages: [{ role: 'user', content: userMessage }], + }); + + for (const block of response.content) { + if (block.type === 'tool_use') { + const result = await executeTool(block.name, block.input, editor); + console.log(`Tool ${block.name}:`, result.success ? 'Success' : 'Failed'); + } + } +} + +await processUserRequest('Add a paragraph saying "Hello World"'); +``` + +## Comparison with AI Actions + +| Feature | AI Builder | AI Actions | +| --------------- | -------------------- | -------------------- | +| **Use Case** | Custom workflows | Pre-built operations | +| **Complexity** | Low-level primitives | High-level methods | +| **Flexibility** | Maximum | Fixed operations | +| **Setup** | More code required | Minimal setup | +| **Best For** | Advanced use cases | Quick integration | + +## Related + +- [AI Actions](../ai-actions.ts) - High-level AI operations +- [SuperDoc Docs](https://docs.superdoc.dev/ai/ai-builder/overview) diff --git a/packages/ai/src/ai-builder/__tests__/helpers/enrichContent.test.ts b/packages/ai/src/ai-builder/__tests__/helpers/enrichContent.test.ts new file mode 100644 index 000000000..8552b286b --- /dev/null +++ b/packages/ai/src/ai-builder/__tests__/helpers/enrichContent.test.ts @@ -0,0 +1,118 @@ +import { describe, it, expect } from 'vitest'; +import { enrichParagraphNodes } from '../../helpers/enrichContent'; + +describe('enrichParagraphNodes', () => { + it('should add default spacing to paragraph nodes without spacing', () => { + const input = [{ type: 'paragraph', content: [{ type: 'text', text: 'Hello' }] }]; + + const result = enrichParagraphNodes(input); + + expect(result[0].attrs).toBeDefined(); + expect(result[0].attrs.spacing).toEqual({ + after: null, + before: null, + line: null, + lineRule: 'auto', + }); + }); + + it('should preserve existing spacing attributes', () => { + const input = [ + { + type: 'paragraph', + content: [{ type: 'text', text: 'Hello' }], + attrs: { + spacing: { after: 100, before: 50, line: 120, lineRule: 'exact' }, + }, + }, + ]; + + const result = enrichParagraphNodes(input); + + expect(result[0].attrs.spacing).toEqual({ + after: 100, + before: 50, + line: 120, + lineRule: 'exact', + }); + }); + + it('should preserve other attrs while adding spacing', () => { + const input = [ + { + type: 'paragraph', + content: [{ type: 'text', text: 'Hello' }], + attrs: { styleId: 'Heading1' }, + }, + ]; + + const result = enrichParagraphNodes(input); + + expect(result[0].attrs.styleId).toBe('Heading1'); + expect(result[0].attrs.spacing).toEqual({ + after: null, + before: null, + line: null, + lineRule: 'auto', + }); + }); + + it('should not affect non-paragraph nodes', () => { + const input = [ + { type: 'heading', level: 1, content: [{ type: 'text', text: 'Title' }] }, + { type: 'paragraph', content: [{ type: 'text', text: 'Content' }] }, + ]; + + const result = enrichParagraphNodes(input); + + // Heading should remain unchanged + expect(result[0].type).toBe('heading'); + expect(result[0].attrs).toBeUndefined(); + + // Paragraph should have spacing added + expect(result[1].type).toBe('paragraph'); + expect(result[1].attrs.spacing).toBeDefined(); + }); + + it('should handle empty arrays', () => { + const result = enrichParagraphNodes([]); + expect(result).toEqual([]); + }); + + it('should handle non-array input gracefully', () => { + const result = enrichParagraphNodes(null as any); + expect(result).toBeNull(); + }); + + it('should not mutate original nodes', () => { + const input = [{ type: 'paragraph', content: [{ type: 'text', text: 'Hello' }] }]; + + const original = JSON.parse(JSON.stringify(input)); + const result = enrichParagraphNodes(input); + + // Original should be unchanged + expect(input).toEqual(original); + + // Result should have spacing + expect(result[0].attrs.spacing).toBeDefined(); + }); + + it('should handle multiple paragraph nodes', () => { + const input = [ + { type: 'paragraph', content: [{ type: 'text', text: 'First' }] }, + { type: 'paragraph', content: [{ type: 'text', text: 'Second' }] }, + { type: 'paragraph', content: [{ type: 'text', text: 'Third' }] }, + ]; + + const result = enrichParagraphNodes(input); + + result.forEach((node) => { + expect(node.attrs.spacing).toEqual({ + after: null, + before: null, + line: null, + lineRule: 'auto', + }); + }); + }); +}); diff --git a/packages/ai/src/ai-builder/content-schema.ts b/packages/ai/src/ai-builder/content-schema.ts new file mode 100644 index 000000000..dfa338ea4 --- /dev/null +++ b/packages/ai/src/ai-builder/content-schema.ts @@ -0,0 +1,125 @@ +/** + * Hardcoded content schema for SuperDoc AI + * + * JSON Schema that describes SuperDoc's document structure for LLMs. + * + * Structure: + * - Document = array of paragraphs + * - Paragraph = contains text nodes with optional marks (bold, italic, etc.) + * - Supports lists via numberingProperties attribute + * - Supports headings via styleId attribute + */ + +/** + * The content schema for SuperDoc documents + * + * This is a hardcoded schema. Future versions may generate this dynamically. + */ +export const CONTENT_SCHEMA = { + type: 'array', + description: 'Array of paragraph nodes that make up the document content', + items: { + additionalProperties: false, + type: 'object', + required: ['type', 'content'], + properties: { + type: { + type: 'string', + const: 'paragraph', + description: + 'Paragraph node. For headings, use styleId attribute (e.g., "Heading1"). For lists, use numberingProperties.', + }, + content: { + type: 'array', + description: 'Array of text nodes and line breaks', + items: { + oneOf: [ + { + type: 'object', + required: ['type', 'text'], + properties: { + type: { + type: 'string', + const: 'text', + description: 'Text content node', + }, + text: { + type: 'string', + description: 'The actual text content', + }, + marks: { + type: 'array', + description: 'Optional formatting marks (bold, italic, etc.)', + items: { + type: 'object', + required: ['type'], + properties: { + type: { + type: 'string', + enum: ['bold', 'italic', 'underline', 'strike', 'link', 'highlight', 'textStyle'], + description: 'Type of formatting mark', + }, + attrs: { + type: 'object', + description: 'Mark attributes (e.g., href for links, color for highlights)', + }, + }, + }, + }, + }, + }, + { + type: 'object', + required: ['type'], + properties: { + type: { + type: 'string', + const: 'hardBreak', + description: 'Line break (Shift+Enter)', + }, + }, + }, + ], + }, + }, + attrs: { + type: 'object', + description: 'Paragraph attributes for styling and structure', + properties: { + styleId: { + type: 'string', + description: 'Word style ID for headings (e.g., "Heading1", "Heading2", etc.) or other styles', + }, + textAlign: { + type: 'string', + enum: ['left', 'center', 'right', 'justify'], + description: 'Text alignment', + }, + lineHeight: { + oneOf: [{ type: 'string' }, { type: 'number' }], + description: 'Line height (e.g., "1.5" or 1.5)', + }, + textIndent: { + oneOf: [{ type: 'string' }, { type: 'number' }], + description: 'First-line indentation', + }, + numberingProperties: { + type: 'object', + description: 'List properties. Use numId=1 for bullet lists, numId=2 for numbered lists', + required: ['numId', 'ilvl'], + properties: { + numId: { + type: 'number', + description: 'Numbering definition ID: 1 for bullets, 2 for numbered lists', + }, + ilvl: { + type: 'number', + description: 'Indentation level (0-8, where 0 is top level)', + }, + }, + }, + }, + }, + }, + }, +} as const; diff --git a/packages/ai/src/ai-builder/executor.ts b/packages/ai/src/ai-builder/executor.ts new file mode 100644 index 000000000..edfbfb407 --- /dev/null +++ b/packages/ai/src/ai-builder/executor.ts @@ -0,0 +1,81 @@ +import type { Editor } from '../types'; +import type { ToolResult, ExecuteToolOptions } from './types'; +import { getTool } from './tools'; + +/** + * Execute a tool by name with given parameters. + * This is the primary way to run AI-generated tool calls. + * + * @param toolName - Name of the tool to execute + * @param params - Parameters to pass to the tool + * @param editor - SuperDoc editor instance + * @param options - Optional execution options + * @returns Tool execution result + * + * @example + * ```typescript + * const result = await executeTool('insertContent', { + * position: 'selection', + * content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Hello' }] }] + * }, editor); + * + * if (result.success) { + * console.log('Content inserted successfully'); + * } + * ``` + */ +export async function executeTool( + toolName: string, + params: any, + editor: Editor, + options?: ExecuteToolOptions, +): Promise { + try { + // Check for cancellation + if (options?.signal?.aborted) { + return { + success: false, + error: 'Tool execution was cancelled', + docChanged: false, + }; + } + + // Get the tool + const tool = getTool(toolName); + if (!tool) { + return { + success: false, + error: `Unknown tool: ${toolName}`, + docChanged: false, + }; + } + + // Validate params if requested + if (options?.validate) { + // Basic validation - could be enhanced with JSON Schema validation + if (params === undefined || params === null) { + return { + success: false, + error: 'Tool parameters are required', + docChanged: false, + }; + } + } + + // Execute the tool + const result = await tool.execute(editor, params); + + // Report progress if callback provided + if (options?.onProgress) { + options.onProgress(100); + } + + return result; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error during tool execution', + docChanged: false, + }; + } +} diff --git a/packages/ai/src/ai-builder/helpers/enrichContent.ts b/packages/ai/src/ai-builder/helpers/enrichContent.ts new file mode 100644 index 000000000..2a8664315 --- /dev/null +++ b/packages/ai/src/ai-builder/helpers/enrichContent.ts @@ -0,0 +1,58 @@ +/** + * Default paragraph spacing attributes matching super-editor defaults + * + * These values match getDefaultSpacing() from: + * packages/super-editor/src/extensions/paragraph/helpers/getDefaultSpacing.js + */ +const DEFAULT_SPACING = { + after: null, + before: null, + line: null, + lineRule: 'auto', +}; + +/** + * Add default spacing attributes to paragraph nodes if not already present + * + * This ensures all AI-generated content has consistent paragraph formatting + * matching the defaults used in the paragraph extension. Without spacing + * attributes, paragraphs render with zero margins and no visual line breaks. + * + * @param nodes - Array of content nodes (typically from AI) + * @returns Array of nodes with spacing attributes enriched + * + * @example + * ```typescript + * const enriched = enrichParagraphNodes([ + * { type: 'paragraph', content: [{ type: 'text', text: 'Hello' }] } + * ]); + * // Returns nodes with default spacing added to attrs + * ``` + */ +export function enrichParagraphNodes(nodes: any[]): any[] { + if (!Array.isArray(nodes)) { + return nodes; + } + + return nodes.map((node) => { + // Only process paragraph nodes + if (node?.type !== 'paragraph') { + return node; + } + + // Create a copy to avoid mutating the original + const enrichedNode = { ...node }; + + // Initialize attrs if not present + if (!enrichedNode.attrs) { + enrichedNode.attrs = {}; + } + + // Add default spacing if not already set + if (!enrichedNode.attrs.spacing) { + enrichedNode.attrs.spacing = { ...DEFAULT_SPACING }; + } + + return enrichedNode; + }); +} diff --git a/packages/ai/src/ai-builder/helpers/getDocumentContext.ts b/packages/ai/src/ai-builder/helpers/getDocumentContext.ts new file mode 100644 index 000000000..fa6ee2059 --- /dev/null +++ b/packages/ai/src/ai-builder/helpers/getDocumentContext.ts @@ -0,0 +1,75 @@ +import type { Editor } from '../../types'; + +/** + * Result from getDocumentContext + */ +export interface DocumentContextResult { + /** Strategy used: 'full' for small docs, 'selection' for large docs */ + strategy: 'full' | 'selection'; + /** The document content (full or selection only) */ + content: unknown; + /** Guidance message for large documents */ + message?: string; +} + +/** + * Options for getDocumentContext + */ +export interface DocumentContextOptions { + /** Maximum tokens before switching to selection-only mode (default: 5000) */ + maxTokens?: number; +} + +/** + * Get document context optimized for token efficiency. + * + * - Small documents: returns full document content + * - Large documents: returns only selection, with guidance to use tools + * + * @example + * ```typescript + * const context = getDocumentContext(editor, { maxTokens: 5000 }); + * + * if (context.strategy === 'full') { + * // Send full document to LLM + * systemPrompt += `\n\nDocument:\n${JSON.stringify(context.content)}`; + * } else { + * // Send selection + guidance + * systemPrompt += `\n\nSelected content:\n${JSON.stringify(context.content)}`; + * systemPrompt += `\n\n${context.message}`; + * } + * ``` + */ +export function getDocumentContext(editor: Editor, options?: DocumentContextOptions): DocumentContextResult { + const maxTokens = options?.maxTokens ?? 5000; + const charsPerToken = 4; // rough estimate + + const { state } = editor; + if (!state) { + return { + strategy: 'full', + content: null, + }; + } + + const docSize = state.doc.content.size; + const estimatedTokens = Math.ceil(docSize / charsPerToken); + + // Small document: return full content + if (estimatedTokens <= maxTokens) { + return { + strategy: 'full', + content: state.doc.toJSON(), + }; + } + + // Large document: return selection only + const { from, to } = state.selection; + const selectedContent = state.doc.cut(from, to); + + return { + strategy: 'selection', + content: selectedContent.toJSON(), + message: 'Document is large. Use searchContent to find text positions and readContent to read specific sections.', + }; +} diff --git a/packages/ai/src/ai-builder/index.ts b/packages/ai/src/ai-builder/index.ts new file mode 100644 index 000000000..252e3c6d3 --- /dev/null +++ b/packages/ai/src/ai-builder/index.ts @@ -0,0 +1,59 @@ +/** + * SuperDoc AI Builder - Low-level primitives for building custom AI workflows + * + * @module ai-builder + * + * AI Builder provides the foundational components for creating AI-powered + * document editing experiences. It offers: + * + * - **Tools**: Core document operations (read, search, insert, replace) + * - **Executor**: Primitive for running tool calls (executeTool) + * - **Provider**: Anthropic tool schemas + * - **Helper**: Token-efficient document context (getDocumentContext) + * + * @example + * ```typescript + * import { executeTool, anthropicTools, getDocumentContext } from '@superdoc-dev/ai/ai-builder'; + * import Anthropic from '@anthropic-ai/sdk'; + * + * // Get tool definitions + * const tools = anthropicTools(); + * + * // Get document context (full doc for small, selection for large) + * const context = getDocumentContext(editor, { maxTokens: 5000 }); + * + * // Use with Anthropic SDK + * const anthropic = new Anthropic({ apiKey: '...' }); + * const response = await anthropic.messages.create({ + * model: 'claude-sonnet-4-5', + * system: `You are a document editor.\n\nDocument:\n${JSON.stringify(context.content)}`, + * tools, + * messages: [{ role: 'user', content: userMessage }] + * }); + * + * // Execute tool calls + * for (const toolUse of response.content.filter(c => c.type === 'tool_use')) { + * await executeTool(toolUse.name, toolUse.input, editor); + * } + * ``` + */ + +// Core types +export type * from './types'; + +// Tools +export * from './tools/index'; + +// Executor +export { executeTool } from './executor'; + +// Providers +export { anthropicTools } from './providers/anthropic'; +export * from './providers/index'; + +// Content schema +export { CONTENT_SCHEMA } from './content-schema'; + +// Helpers +export { getDocumentContext } from './helpers/getDocumentContext'; +export type { DocumentContextResult, DocumentContextOptions } from './helpers/getDocumentContext'; diff --git a/packages/ai/src/ai-builder/providers/anthropic.ts b/packages/ai/src/ai-builder/providers/anthropic.ts new file mode 100644 index 000000000..463c7ccb0 --- /dev/null +++ b/packages/ai/src/ai-builder/providers/anthropic.ts @@ -0,0 +1,209 @@ +import type { AnthropicTool, ToolDefinitionsOptions } from '../types'; + +/** + * Generate Anthropic-compatible tool definitions for SuperDoc AI. + * + * Returns an array of tool objects compatible with Anthropic's Messages API. + * + * @param extensions - Array of SuperDoc extensions (unused for now, reserved for future) + * @param options - Tool definition options + * @returns Array of Anthropic tool definitions + * + * @example + * ```typescript + * import { anthropicTools } from '@superdoc-dev/ai/ai-builder/providers'; + * import Anthropic from '@anthropic-ai/sdk'; + * + * const tools = anthropicTools(); + * + * const anthropic = new Anthropic({ apiKey: '...' }); + * const response = await anthropic.messages.create({ + * model: 'claude-sonnet-4-5', + * tools, + * messages: [...] + * }); + * ``` + */ +export function anthropicTools(extensions: unknown[] = [], options?: ToolDefinitionsOptions): AnthropicTool[] { + const { enabledTools } = options || {}; + + // Define all available tools + const allTools: AnthropicTool[] = [ + { + name: 'readSelection', + description: + 'Read the currently selected content in the document. Returns the selection range (from/to positions) and the JSON representation. Use withContext to include surrounding paragraphs.', + input_schema: { + type: 'object', + properties: { + withContext: { + type: 'integer', + description: 'Number of paragraphs to include before and after the selection for context (optional)', + }, + }, + required: [], + additionalProperties: false, + }, + }, + { + name: 'readContent', + description: + 'Read document content at a specific position range (from/to character offsets). Use after searchContent to read actual content at found positions.', + input_schema: { + type: 'object', + properties: { + from: { + type: 'integer', + description: 'Start position (character offset)', + }, + to: { + type: 'integer', + description: 'End position (character offset)', + }, + }, + required: ['from', 'to'], + additionalProperties: false, + }, + }, + { + name: 'searchContent', + description: + 'Search for text or patterns in the document. Returns matches with their positions (from/to character offsets). Use with readContent to see context or replaceContent to modify.', + input_schema: { + type: 'object', + properties: { + query: { + type: 'string', + description: 'The text or pattern to search for', + }, + caseSensitive: { + type: 'boolean', + description: 'Whether the search should be case-sensitive (default: false)', + }, + regex: { + type: 'boolean', + description: 'Whether to treat query as a regular expression (default: false)', + }, + findAll: { + type: 'boolean', + description: 'Whether to return all matches or just the first one (default: true)', + }, + }, + required: ['query'], + additionalProperties: false, + }, + }, + { + name: 'getContentSchema', + description: + 'Get the JSON schema for document content format. Call this before insertContent or replaceContent to understand the expected structure for the content array.', + input_schema: { + type: 'object', + properties: {}, + required: [], + additionalProperties: false, + }, + }, + { + name: 'insertContent', + description: + 'Insert new content into the document. Call getContentSchema first to understand the content format. Position can be "selection" (at cursor), "documentStart", or "documentEnd".', + input_schema: { + type: 'object', + properties: { + position: { + type: 'string', + enum: ['selection', 'documentStart', 'documentEnd'], + description: 'Where to insert the content', + }, + content: { + type: 'array', + description: 'Array of paragraph nodes. Call getContentSchema for the full format specification.', + }, + }, + required: ['position', 'content'], + additionalProperties: false, + }, + }, + { + name: 'replaceContent', + description: + 'Replace content in the document. Use query to search and replace text by name, or use from/to for exact positions.', + input_schema: { + type: 'object', + properties: { + query: { + type: 'string', + description: 'Text to search for and replace. Use this instead of from/to positions.', + }, + from: { + type: 'integer', + description: 'Start position (character offset). Only needed if query is not provided.', + }, + to: { + type: 'integer', + description: 'End position (character offset). Only needed if query is not provided.', + }, + content: { + type: 'array', + description: 'Array of paragraph nodes to replace with.', + }, + replaceAll: { + type: 'boolean', + description: 'Whether to replace all occurrences when using query (default: false)', + }, + }, + required: ['content'], + additionalProperties: false, + }, + }, + { + name: 'getDocumentOutline', + description: + 'Get the document outline (headings and their positions). Use this to understand document structure before reading or editing specific sections.', + input_schema: { + type: 'object', + properties: {}, + required: [], + additionalProperties: false, + }, + }, + { + name: 'readSection', + description: + 'Read a specific section of the document by heading name or position range. Use heading parameter to find by name, or from/to for exact positions.', + input_schema: { + type: 'object', + properties: { + heading: { + type: 'string', + description: + 'Heading text to find and read (case-insensitive partial match). The section includes content until the next heading of same or higher level.', + }, + from: { + type: 'integer', + description: 'Start position (character offset). Alternative to heading parameter.', + }, + to: { + type: 'integer', + description: 'End position (character offset). Alternative to heading parameter.', + }, + }, + required: [], + additionalProperties: false, + }, + }, + ]; + + // Filter tools if enabledTools is specified + if (enabledTools && enabledTools.length > 0) { + return allTools.filter((tool) => enabledTools.includes(tool.name)); + } + + return allTools; +} + +/** + * Alias for anthropicTools for consistency + */ +export const toolDefinitions = anthropicTools; diff --git a/packages/ai/src/ai-builder/providers/index.ts b/packages/ai/src/ai-builder/providers/index.ts new file mode 100644 index 000000000..b4fe22352 --- /dev/null +++ b/packages/ai/src/ai-builder/providers/index.ts @@ -0,0 +1 @@ +export { anthropicTools, toolDefinitions as anthropicToolDefinitions } from './anthropic'; diff --git a/packages/ai/src/ai-builder/tools/getContentSchema.ts b/packages/ai/src/ai-builder/tools/getContentSchema.ts new file mode 100644 index 000000000..546a18430 --- /dev/null +++ b/packages/ai/src/ai-builder/tools/getContentSchema.ts @@ -0,0 +1,36 @@ +import type { Editor } from '../../types'; +import type { SuperDocTool, ToolResult } from '../types'; +import { CONTENT_SCHEMA } from '../content-schema'; + +/** + * Tool for getting the content schema. + * Call this before insertContent or replaceContent to understand the expected format. + * + * @example + * // First get the schema + * const schema = await executeTool('getContentSchema', {}, editor); + * // Then use the format to create content + * await executeTool('insertContent', { + * position: 'selection', + * content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Hello' }] }] + * }, editor); + */ +export const getContentSchema: SuperDocTool = { + name: 'getContentSchema', + description: + 'Get the JSON schema that describes the expected format for document content. Call this before using insertContent or replaceContent to understand how to structure the content array.', + category: 'read', + + async execute(_editor: Editor): Promise { + return { + success: true, + data: { + schema: CONTENT_SCHEMA, + summary: + 'Content is an array of paragraph objects. Each paragraph has type="paragraph", optional attrs (styleId for headings, numberingProperties for lists), and content array of text nodes with optional marks (bold, italic, etc.).', + }, + docChanged: false, + message: 'Content schema returned. Use this format for insertContent and replaceContent.', + }; + }, +}; diff --git a/packages/ai/src/ai-builder/tools/getDocumentOutline.ts b/packages/ai/src/ai-builder/tools/getDocumentOutline.ts new file mode 100644 index 000000000..02e0a6944 --- /dev/null +++ b/packages/ai/src/ai-builder/tools/getDocumentOutline.ts @@ -0,0 +1,91 @@ +import type { Node } from 'prosemirror-model'; +import type { Editor } from '../../types'; +import type { SuperDocTool, ToolResult } from '../types'; + +/** + * Heading info returned in document outline + */ +export interface HeadingInfo { + /** The heading text */ + text: string; + /** Heading level (1-6) */ + level: number; + /** Start position in document */ + position: number; +} + +/** + * Tool for getting document structure/outline. + * Returns headings with their positions so LLM can navigate large documents. + * + * @example + * const outline = await executeTool('getDocumentOutline', {}, editor); + * // Returns: { headings: [{ text: "Introduction", level: 1, position: 0 }, ...], totalLength: 5000 } + */ +export const getDocumentOutline: SuperDocTool = { + name: 'getDocumentOutline', + description: + 'Get the document outline (headings and their positions). Use this to understand document structure before reading or editing specific sections.', + category: 'read', + + async execute(editor: Editor): Promise { + try { + const { state } = editor; + if (!state) { + return { + success: false, + error: 'Editor state not available', + docChanged: false, + }; + } + + const headings: HeadingInfo[] = []; + const doc = state.doc; + + // Walk through document to find headings + doc.descendants((node: Node, pos: number) => { + if (node.type.name === 'paragraph') { + const styleId = node.attrs?.styleId; + if (styleId && typeof styleId === 'string') { + // Check for Heading1, Heading2, etc. + const match = styleId.match(/^Heading(\d)$/i); + if (match) { + const level = parseInt(match[1], 10); + // Extract text content from the paragraph + let text = ''; + node.content.forEach((child: Node) => { + if (child.isText) { + text += child.text; + } + }); + + headings.push({ + text: text.trim() || '(untitled)', + level, + position: pos, + }); + } + } + } + return true; // continue traversal + }); + + return { + success: true, + data: { + headings, + totalLength: doc.content.size, + headingCount: headings.length, + }, + docChanged: false, + message: `Found ${headings.length} headings in document`, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to get document outline', + docChanged: false, + }; + } + }, +}; diff --git a/packages/ai/src/ai-builder/tools/index.ts b/packages/ai/src/ai-builder/tools/index.ts new file mode 100644 index 000000000..673cd8b76 --- /dev/null +++ b/packages/ai/src/ai-builder/tools/index.ts @@ -0,0 +1,54 @@ +export { readSelection } from './readSelection'; +export { readContent } from './readContent'; +export { searchContent } from './searchContent'; +export { getContentSchema } from './getContentSchema'; +export { insertContent } from './insertContent'; +export { replaceContent } from './replaceContent'; +export { getDocumentOutline } from './getDocumentOutline'; +export { readSection } from './readSection'; + +export type { ReadSelectionParams } from './readSelection'; +export type { ReadContentParams } from './readContent'; +export type { SearchContentParams, SearchMatch } from './searchContent'; +export type { InsertContentParams } from './insertContent'; +export type { ReplaceContentParams } from './replaceContent'; +export type { HeadingInfo } from './getDocumentOutline'; +export type { ReadSectionParams } from './readSection'; + +import { readSelection } from './readSelection'; +import { readContent } from './readContent'; +import { searchContent } from './searchContent'; +import { getContentSchema } from './getContentSchema'; +import { insertContent } from './insertContent'; +import { replaceContent } from './replaceContent'; +import { getDocumentOutline } from './getDocumentOutline'; +import { readSection } from './readSection'; +import type { SuperDocTool } from '../types'; + +/** + * All available SuperDoc AI tools + */ +export const ALL_TOOLS: Record = { + readSelection, + readContent, + searchContent, + getContentSchema, + insertContent, + replaceContent, + getDocumentOutline, + readSection, +}; + +/** + * Get a tool by name + */ +export function getTool(name: string): SuperDocTool | undefined { + return ALL_TOOLS[name]; +} + +/** + * Get all tool names + */ +export function getToolNames(): string[] { + return Object.keys(ALL_TOOLS); +} diff --git a/packages/ai/src/ai-builder/tools/insertContent.ts b/packages/ai/src/ai-builder/tools/insertContent.ts new file mode 100644 index 000000000..38c34ba45 --- /dev/null +++ b/packages/ai/src/ai-builder/tools/insertContent.ts @@ -0,0 +1,104 @@ +import type { Editor } from '../../types'; +import type { SuperDocTool, ToolResult } from '../types'; +import { enrichParagraphNodes } from '../helpers/enrichContent'; + +/** + * Params for insertContent tool + */ +export interface InsertContentParams { + /** Where to insert: 'selection' (at cursor), 'documentStart', or 'documentEnd' */ + position: 'selection' | 'documentStart' | 'documentEnd'; + /** Array of content nodes to insert (ProseMirror JSON format) */ + content: any[]; +} + +/** + * Tool for inserting content at specified positions in the document. + * Supports inserting at cursor position, document start, or document end. + */ +export const insertContent: SuperDocTool = { + name: 'insertContent', + description: + 'Insert new content into the document. Position can be "selection" (at cursor), "documentStart", or "documentEnd". Content should be an array of paragraph blocks in ProseMirror JSON format.', + category: 'write', + + async execute(editor: Editor, params: InsertContentParams): Promise { + try { + const { position, content } = params; + + if (!content || !Array.isArray(content)) { + return { + success: false, + error: 'Content must be an array of nodes', + docChanged: false, + }; + } + + // Automatically add default spacing attributes to paragraph nodes + const enrichedContent = enrichParagraphNodes(content); + + const { state } = editor; + if (!state) { + return { + success: false, + error: 'Editor state not available', + docChanged: false, + }; + } + + let insertPos: number; + switch (position) { + case 'selection': + insertPos = state.selection.from; + break; + case 'documentStart': + insertPos = 0; + break; + case 'documentEnd': + insertPos = state.doc.content.size; + break; + default: + return { + success: false, + error: `Invalid position: ${position}`, + docChanged: false, + }; + } + + // Use editor's insertContentAt command + // For single nodes, pass directly; for multiple, wrap in an array + let insertSuccess: boolean; + + if (Array.isArray(enrichedContent) && enrichedContent.length === 1) { + // Single node: pass directly + insertSuccess = editor.commands.insertContentAt(insertPos, enrichedContent[0]); + } else { + // Multiple nodes or array: pass as-is + insertSuccess = editor.commands.insertContentAt(insertPos, enrichedContent); + } + + const success = insertSuccess; + + if (!success) { + return { + success: false, + error: 'Failed to insert content', + docChanged: false, + }; + } + + return { + success: true, + data: { insertedAt: insertPos }, + docChanged: true, + message: `Inserted ${enrichedContent.length} node(s) at ${position}`, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + docChanged: false, + }; + } + }, +}; diff --git a/packages/ai/src/ai-builder/tools/readContent.ts b/packages/ai/src/ai-builder/tools/readContent.ts new file mode 100644 index 000000000..b5b515012 --- /dev/null +++ b/packages/ai/src/ai-builder/tools/readContent.ts @@ -0,0 +1,87 @@ +import type { Editor } from '../../types'; +import type { SuperDocTool, ToolResult } from '../types'; + +/** + * Params for readContent tool + */ +export interface ReadContentParams { + /** Start position (character offset) */ + from: number; + /** End position (character offset) */ + to: number; +} + +/** + * Tool for reading content at a specific position range. + * Use this after searchContent to read the actual content around a found position. + * + * @example + * // First find the position + * const searchResult = await executeTool('searchContent', { query: 'Introduction' }, editor); + * // Then read the content around it + * const content = await executeTool('readContent', { + * from: searchResult.data.matches[0].from, + * to: searchResult.data.matches[0].to + 500 // read 500 chars after + * }, editor); + */ +export const readContent: SuperDocTool = { + name: 'readContent', + description: + 'Read document content at a specific position range (from/to character offsets). Use after searchContent to read actual content at found positions.', + category: 'read', + + async execute(editor: Editor, params: ReadContentParams): Promise { + try { + const { from, to } = params; + + if (typeof from !== 'number' || typeof to !== 'number') { + return { + success: false, + error: 'Both "from" and "to" parameters must be numbers', + docChanged: false, + }; + } + + if (from < 0 || to < from) { + return { + success: false, + error: 'Invalid range: "from" must be >= 0 and "to" must be >= "from"', + docChanged: false, + }; + } + + const { state } = editor; + if (!state) { + return { + success: false, + error: 'Editor state not available', + docChanged: false, + }; + } + + // Clamp positions to document bounds + const docSize = state.doc.content.size; + const clampedFrom = Math.min(from, docSize); + const clampedTo = Math.min(to, docSize); + + const content = state.doc.cut(clampedFrom, clampedTo); + + return { + success: true, + data: { + from: clampedFrom, + to: clampedTo, + content: content.toJSON(), + }, + docChanged: false, + message: `Read content from position ${clampedFrom} to ${clampedTo}`, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to read content', + docChanged: false, + }; + } + }, +}; diff --git a/packages/ai/src/ai-builder/tools/readSection.ts b/packages/ai/src/ai-builder/tools/readSection.ts new file mode 100644 index 000000000..1653defb4 --- /dev/null +++ b/packages/ai/src/ai-builder/tools/readSection.ts @@ -0,0 +1,146 @@ +import type { Node } from 'prosemirror-model'; +import type { Editor } from '../../types'; +import type { SuperDocTool, ToolResult } from '../types'; + +/** + * Params for readSection tool + */ +export interface ReadSectionParams { + /** Heading text to find and read (case-insensitive partial match) */ + heading?: string; + /** Start position (alternative to heading) */ + from?: number; + /** End position (alternative to heading) */ + to?: number; +} + +/** + * Tool for reading a specific section of the document by heading name. + * Use after getDocumentOutline to read content of a specific section. + * + * @example + * // Read by heading name + * const section = await executeTool('readSection', { heading: 'Introduction' }, editor); + * + * // Read by position (from outline) + * const section = await executeTool('readSection', { from: 150, to: 450 }, editor); + */ +export const readSection: SuperDocTool = { + name: 'readSection', + description: + 'Read a specific section of the document by heading name or position range. Use heading parameter to find by name, or from/to for exact positions.', + category: 'read', + + async execute(editor: Editor, params: ReadSectionParams): Promise { + try { + const { heading, from, to } = params; + const { state } = editor; + + if (!state) { + return { + success: false, + error: 'Editor state not available', + docChanged: false, + }; + } + + const doc = state.doc; + + // If from/to provided, read that range directly + if (typeof from === 'number' && typeof to === 'number') { + const clampedFrom = Math.max(0, Math.min(from, doc.content.size)); + const clampedTo = Math.max(clampedFrom, Math.min(to, doc.content.size)); + const content = doc.cut(clampedFrom, clampedTo); + + return { + success: true, + data: { + from: clampedFrom, + to: clampedTo, + content: content.toJSON(), + }, + docChanged: false, + message: `Read section from position ${clampedFrom} to ${clampedTo}`, + }; + } + + // Find section by heading name + if (!heading) { + return { + success: false, + error: 'Either "heading" or "from"/"to" parameters are required', + docChanged: false, + }; + } + + const searchTerm = heading.toLowerCase(); + let sectionStart: number | null = null; + let sectionEnd: number | null = null; + let foundHeadingLevel: number | null = null; + let foundHeadingText: string | null = null; + + // Find the heading and the next heading at same or higher level + doc.descendants((node: Node, pos: number) => { + if (node.type.name === 'paragraph') { + const styleId = node.attrs?.styleId; + if (styleId && typeof styleId === 'string') { + const match = styleId.match(/^Heading(\d)$/i); + if (match) { + const level = parseInt(match[1], 10); + let text = ''; + node.content.forEach((child: Node) => { + if (child.isText) text += child.text; + }); + + // If we haven't found our section yet, look for matching heading + if (sectionStart === null) { + if (text.toLowerCase().includes(searchTerm)) { + sectionStart = pos; + foundHeadingLevel = level; + foundHeadingText = text.trim(); + } + } else { + // We found our section, now look for end (same or higher level heading) + if (level <= foundHeadingLevel!) { + sectionEnd = pos; + return false; // stop traversal + } + } + } + } + } + return true; + }); + + if (sectionStart === null) { + return { + success: false, + error: `No heading found matching "${heading}"`, + docChanged: false, + }; + } + + // If no end found, section goes to end of document + const finalEnd = sectionEnd ?? doc.content.size; + const content = doc.cut(sectionStart, finalEnd); + + return { + success: true, + data: { + heading: foundHeadingText, + from: sectionStart, + to: finalEnd, + content: content.toJSON(), + }, + docChanged: false, + message: `Read section "${foundHeadingText}" (positions ${sectionStart}-${finalEnd})`, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to read section', + docChanged: false, + }; + } + }, +}; diff --git a/packages/ai/src/ai-builder/tools/readSelection.ts b/packages/ai/src/ai-builder/tools/readSelection.ts new file mode 100644 index 000000000..058f434a2 --- /dev/null +++ b/packages/ai/src/ai-builder/tools/readSelection.ts @@ -0,0 +1,100 @@ +import type { Node } from 'prosemirror-model'; +import type { Editor } from '../../types'; +import type { SuperDocTool, ToolResult } from '../types'; + +/** + * Params for readSelection tool + */ +export interface ReadSelectionParams { + /** Number of paragraphs to include before and after the selection for context */ + withContext?: number; +} + +/** + * Tool for reading the currently selected content in the document. + * Returns the selection range and the JSON representation of the selected content. + * Optionally includes surrounding paragraphs for context. + * + * @example + * // Read just the selection + * const selection = await executeTool('readSelection', {}, editor); + * + * // Read selection with 2 paragraphs before/after for context + * const selection = await executeTool('readSelection', { withContext: 2 }, editor); + */ +export const readSelection: SuperDocTool = { + name: 'readSelection', + description: + 'Read the currently selected content in the document. Returns the selection range (from/to positions) and the JSON representation. Use withContext to include surrounding paragraphs.', + category: 'read', + + async execute(editor: Editor, params?: ReadSelectionParams): Promise { + try { + const { state } = editor; + if (!state) { + return { + success: false, + error: 'Editor state not available', + docChanged: false, + }; + } + + const { from, to } = state.selection; + const selectedContent = state.doc.cut(from, to); + const doc = state.doc; + + const result: { + from: number; + to: number; + content: any; + before?: any; + after?: any; + } = { + from, + to, + content: selectedContent.toJSON(), + }; + + // If withContext is specified, get surrounding paragraphs + const contextCount = params?.withContext; + if (contextCount && contextCount > 0) { + // Get paragraphs before selection + const beforeParagraphs: { position: number; content: ReturnType }[] = []; + doc.nodesBetween(0, from, (node: Node, pos: number) => { + if (node.type.name === 'paragraph') { + beforeParagraphs.push({ + position: pos, + content: node.toJSON(), + }); + } + return true; + }); + // Take the last N paragraphs before selection + result.before = beforeParagraphs.slice(-contextCount).map((p) => p.content); + + // Get paragraphs after selection + const afterParagraphs: ReturnType[] = []; + doc.nodesBetween(to, doc.content.size, (node: Node, _pos: number) => { + if (node.type.name === 'paragraph' && afterParagraphs.length < contextCount) { + afterParagraphs.push(node.toJSON()); + } + return afterParagraphs.length < contextCount; + }); + result.after = afterParagraphs; + } + + return { + success: true, + data: result, + docChanged: false, + message: `Selection from position ${from} to ${to}${contextCount ? ` with ${contextCount} paragraphs context` : ''}`, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + docChanged: false, + }; + } + }, +}; diff --git a/packages/ai/src/ai-builder/tools/replaceContent.ts b/packages/ai/src/ai-builder/tools/replaceContent.ts new file mode 100644 index 000000000..2fe3dec71 --- /dev/null +++ b/packages/ai/src/ai-builder/tools/replaceContent.ts @@ -0,0 +1,169 @@ +import type { Editor } from '../../types'; +import type { SuperDocTool, ToolResult } from '../types'; +import { enrichParagraphNodes } from '../helpers/enrichContent'; + +/** + * Params for replaceContent tool + */ +export interface ReplaceContentParams { + /** Text to search for and replace (alternative to from/to positions) */ + query?: string; + /** Start position (character offset) - required if query not provided */ + from?: number; + /** End position (character offset) - required if query not provided */ + to?: number; + /** Array of content nodes to replace with (ProseMirror JSON format) */ + content: any[]; + /** Whether to replace all occurrences when using query (default: false) */ + replaceAll?: boolean; +} + +/** + * Tool for replacing content in a specific range of the document. + * Removes content from 'from' to 'to' positions and inserts new content. + */ +export const replaceContent: SuperDocTool = { + name: 'replaceContent', + description: + 'Replace content in the document. Either provide a query to search and replace text, or specify exact from/to positions.', + category: 'write', + + async execute(editor: Editor, params: ReplaceContentParams): Promise { + try { + const { query, from, to, content, replaceAll = false } = params; + + if (!content || !Array.isArray(content)) { + return { + success: false, + error: 'Content must be an array of nodes', + docChanged: false, + }; + } + + const { state } = editor; + if (!state) { + return { + success: false, + error: 'Editor state not available', + docChanged: false, + }; + } + + // Automatically add default spacing attributes to paragraph nodes + const enrichedContent = enrichParagraphNodes(content); + + // If query is provided, search for it first + if (query) { + if (!editor.commands?.search) { + return { + success: false, + error: 'Search command not available in editor', + docChanged: false, + }; + } + + const matches = editor.commands.search(query); + if (!matches || !Array.isArray(matches) || matches.length === 0) { + return { + success: false, + error: `No matches found for "${query}"`, + docChanged: false, + }; + } + + // For inline text replacement, extract text content from paragraphs + // This prevents splitting paragraphs when replacing text within them + let inlineContent = enrichedContent; + if ( + enrichedContent.length === 1 && + enrichedContent[0].type === 'paragraph' && + Array.isArray(enrichedContent[0].content) + ) { + // Extract just the inline content (text nodes) from the paragraph + inlineContent = enrichedContent[0].content; + } + + // Replace matches (in reverse order to maintain positions) + const matchesToReplace = replaceAll ? [...matches].reverse() : [matches[0]]; + let replacedCount = 0; + + for (const match of matchesToReplace) { + const success = editor.commands.insertContentAt({ from: match.from, to: match.to }, inlineContent); + if (success) replacedCount++; + } + + return { + success: replacedCount > 0, + data: { + replacedCount, + totalMatches: matches.length, + query, + }, + docChanged: replacedCount > 0, + message: `Replaced ${replacedCount} of ${matches.length} occurrence(s) of "${query}"`, + }; + } + + // Otherwise, use explicit from/to positions + if (typeof from !== 'number' || typeof to !== 'number') { + return { + success: false, + error: 'Either query or from/to positions must be provided', + docChanged: false, + }; + } + + if (from < 0 || to < from) { + return { + success: false, + error: 'Invalid range: from must be >= 0 and to must be >= from', + docChanged: false, + }; + } + + // Clamp positions to valid document range + const docSize = state.doc.content.size; + const validFrom = Math.max(0, Math.min(from, docSize)); + const validTo = Math.max(0, Math.min(to, docSize)); + + // For full document replacement, use setContent + if (validFrom === 0 && validTo === docSize) { + const success = editor.commands.setContent({ type: 'doc', content: enrichedContent }); + + return { + success, + data: { replacedRange: { from: validFrom, to: validTo } }, + docChanged: success, + message: success ? 'Replaced entire document' : 'Failed to replace document', + }; + } + + // For partial replacement, use insertContentAt + const success = editor.commands.insertContentAt( + { from: validFrom, to: validTo }, + { type: 'doc', content: enrichedContent }, + ); + + if (!success) { + return { + success: false, + error: 'Failed to replace content', + docChanged: false, + }; + } + + return { + success: true, + data: { replacedRange: { from: validFrom, to: validTo } }, + docChanged: true, + message: `Replaced content from position ${validFrom} to ${validTo}`, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + docChanged: false, + }; + } + }, +}; diff --git a/packages/ai/src/ai-builder/tools/searchContent.ts b/packages/ai/src/ai-builder/tools/searchContent.ts new file mode 100644 index 000000000..a71acbedb --- /dev/null +++ b/packages/ai/src/ai-builder/tools/searchContent.ts @@ -0,0 +1,144 @@ +import type { Editor } from '../../types'; +import type { SuperDocTool, ToolResult } from '../types'; + +/** + * Params for searchContent tool + */ +export interface SearchContentParams { + /** The text or pattern to search for */ + query: string; + /** Whether the search should be case-sensitive (default: false) */ + caseSensitive?: boolean; + /** Whether to treat query as a regular expression (default: false) */ + regex?: boolean; + /** Whether to return all matches or just the first one (default: true) */ + findAll?: boolean; +} + +/** + * Search result containing match information + */ +export interface SearchMatch { + /** The matched text */ + text: string; + /** Start position in the document */ + from: number; + /** End position in the document */ + to: number; +} + +/** + * Tool for searching text in the document. + * Returns positions of matches that can be used with readContent or replaceContent. + * + * @example + * // Search for all occurrences of "privacy" + * const result = await executeTool('searchContent', { + * query: 'privacy', + * caseSensitive: false, + * findAll: true + * }, editor); + * // Returns: { matches: [{ text: 'privacy', from: 100, to: 107 }, ...] } + * + * // Then read content around the match: + * await executeTool('readContent', { + * from: result.data.matches[0].from - 50, + * to: result.data.matches[0].to + 50 + * }, editor); + * + * // Or replace it: + * await executeTool('replaceContent', { + * from: result.data.matches[0].from, + * to: result.data.matches[0].to, + * content: [{ type: 'paragraph', content: [{ type: 'text', text: 'confidentiality' }] }] + * }, editor); + */ +export const searchContent: SuperDocTool = { + name: 'searchContent', + description: + 'Search for text or patterns in the document. Returns an array of matches with their positions (from/to character offsets). Use with readContent to see context or replaceContent to modify.', + category: 'read', + + async execute(editor: Editor, params: SearchContentParams): Promise { + try { + const { query, caseSensitive = false, regex = false, findAll = true } = params; + + if (!query) { + return { + success: false, + error: 'Query parameter is required', + docChanged: false, + }; + } + + // Check if editor has search command + if (!editor.commands?.search) { + return { + success: false, + error: 'Search command not available in editor', + docChanged: false, + }; + } + + // Create search pattern + let pattern: string | RegExp; + if (regex) { + try { + pattern = new RegExp(query, caseSensitive ? '' : 'i'); + } catch (error) { + return { + success: false, + error: `Invalid regular expression: ${error instanceof Error ? error.message : 'Unknown error'}`, + docChanged: false, + }; + } + } else { + pattern = query; + } + + // Execute search + const rawMatches = editor.commands.search(pattern); + + if (!rawMatches || !Array.isArray(rawMatches)) { + return { + success: false, + error: 'Search command returned invalid results', + docChanged: false, + }; + } + + // Format results + const matches: SearchMatch[] = rawMatches.map((match) => ({ + text: match.text, + from: match.from, + to: match.to, + })); + + // Filter case sensitivity if needed (for non-regex searches) + let filteredMatches = matches; + if (!regex && caseSensitive) { + filteredMatches = matches.filter((match) => match.text === query); + } + + // Return only first match if findAll is false + const finalMatches = findAll ? filteredMatches : filteredMatches.slice(0, 1); + + return { + success: true, + data: { + matches: finalMatches, + count: finalMatches.length, + query, + }, + docChanged: false, + message: `Found ${finalMatches.length} match(es) for "${query}"`, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Search failed', + docChanged: false, + }; + } + }, +}; diff --git a/packages/ai/src/ai-builder/types.ts b/packages/ai/src/ai-builder/types.ts new file mode 100644 index 000000000..2f1204bca --- /dev/null +++ b/packages/ai/src/ai-builder/types.ts @@ -0,0 +1,97 @@ +import type { Editor } from '../types'; + +/** + * Result of executing a tool + */ +export interface ToolResult { + /** Whether the tool executed successfully */ + success: boolean; + /** Data returned by the tool */ + data?: any; + /** Error message if execution failed */ + error?: string; + /** Whether the document was modified */ + docChanged: boolean; + /** Optional message to send back to the AI */ + message?: string; +} + +/** + * Category of tool operation + */ +export type ToolCategory = 'read' | 'write' | 'navigate' | 'analyze'; + +/** + * Core tool interface that all SuperDoc AI tools must implement + */ +export interface SuperDocTool { + /** Unique identifier for the tool */ + name: string; + /** Human-readable description of what the tool does */ + description: string; + /** Category of operation */ + category: ToolCategory; + /** Execute the tool with given parameters */ + execute: (editor: Editor, params: any) => Promise; +} + +/** + * Options for filtering which tools and features to include + */ +export interface ToolDefinitionsOptions { + /** List of tool names to enable (if undefined, all are enabled) */ + enabledTools?: string[]; + /** Node types to exclude (all others from extensions are included) */ + excludedNodes?: string[]; + /** Mark types to exclude (all others from extensions are included) */ + excludedMarks?: string[]; + /** Attribute names to exclude */ + excludedAttrs?: string[]; + /** Whether to use strict mode (for providers that support it) */ + strict?: boolean; +} + +/** + * Options for tool execution + */ +export interface ExecuteToolOptions { + /** Whether to validate params before execution */ + validate?: boolean; + /** Callback for progress updates during execution */ + onProgress?: (progress: number) => void; + /** Abort signal for cancellation */ + signal?: AbortSignal; +} + +/** + * Generic tool schema format (provider-agnostic) + */ +export interface GenericToolSchema { + name: string; + description: string; + parameters: { + type: 'object'; + properties: Record; + required?: string[]; + additionalProperties?: boolean; + }; +} + +/** + * Anthropic-specific tool format + */ +export interface AnthropicTool { + name: string; + description: string; + input_schema: { + type: 'object'; + properties: Record; + required?: string[]; + additionalProperties?: boolean; + }; +} + +/** + * Union of all provider-specific tool formats + */ +export type ProviderToolDefinition = AnthropicTool | GenericToolSchema; diff --git a/packages/ai/src/index.ts b/packages/ai/src/index.ts index 58cb70695..c3de4066e 100644 --- a/packages/ai/src/index.ts +++ b/packages/ai/src/index.ts @@ -1,7 +1,13 @@ +// AI Actions - High-level AI operations export { AIActions } from './ai-actions'; export { AIActionsService } from './ai-actions-service'; export { EditorAdapter } from './editor-adapter'; +// AI Builder - Low-level primitives for custom AI workflows +export * as AIBuilder from './ai-builder/index'; +export { executeTool, anthropicTools } from './ai-builder/index'; + +// Shared types export * from './types'; export * from './utils'; diff --git a/packages/ai/tsup.config.ts b/packages/ai/tsup.config.ts index cbe2261a1..0c056cc80 100644 --- a/packages/ai/tsup.config.ts +++ b/packages/ai/tsup.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from 'tsup'; export default defineConfig({ entry: ['src/index.ts'], - format: ['cjs'], + format: ['esm'], dts: true, clean: true, // Always clean dist folder before build minify: true, // Minify the output