diff --git a/package-lock.json b/package-lock.json index 60b0563..aeb30b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,11 +14,13 @@ "@hotwired/turbo": "^8.0.2", "@koa/router": "^12.0.1", "@playwright/test": "^1.36.1", - "@sealcode/jdd": "^0.3.5", - "@sealcode/sealgen": "^0.14.16", + "@sealcode/file-manager": "^1.0.2", + "@sealcode/jdd": "^0.4.5", + "@sealcode/sealgen": "^0.15.6", "@sealcode/ts-predicates": "^0.6.2", "@types/kill-port": "^2.0.0", "@types/leaflet": "^1.9.8", + "escape-goat": "^4.0.0", "get-port": "^7.0.0", "js-convert-case": "^4.2.0", "koa-responsive-image-router": "^0.2.24", @@ -27,7 +29,7 @@ "nodemon": "^3.0.1", "object-path": "^0.11.8", "qs": "^6.12.0", - "sealious": "^0.18.3", + "sealious": "^0.19.6", "stimulus": "^2.0.0", "tempstream": "^0.3.15", "vitest": "^1.1.0" @@ -304,9 +306,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", + "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -858,16 +860,30 @@ "ansi-html-stream": "index.js" } }, - "node_modules/@sealcode/jdd": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@sealcode/jdd/-/jdd-0.3.5.tgz", - "integrity": "sha512-VE8FRfmLuSgXIxIWeNvJjND64531EiHXjquHgIBIlbPOUCjp3Vx+nUrEZgyFyI//QN5ieNJVZhwOIV7gnfGNxg==", + "node_modules/@sealcode/file-manager": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@sealcode/file-manager/-/file-manager-1.0.2.tgz", + "integrity": "sha512-BOMgC90QffE9cVFKkLVTjDbUJ5WB9YqcmS4fwqFxgnnC3YlH9xb9rff3iGXSkKOHm0kCeSjq0Ohasxtq/z72WQ==", "dependencies": { - "@sealcode/ts-predicates": "^0.5.3", - "koa-responsive-image-router": "^0.2.19", - "locreq": "^2.4.1", - "marked": "^12.0.0", + "@types/mime-types": "^2.1.4", + "@types/uuid": "^9.0.8", + "locreq": "^3.0.0", "mime-types": "^2.1.35", + "uuid": "^9.0.1" + } + }, + "node_modules/@sealcode/jdd": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@sealcode/jdd/-/jdd-0.4.5.tgz", + "integrity": "sha512-N4DWR/+WEosJwkaul4BGB6CuGRkNHAfbNF5hGTCwYZRIFStYCThMGg2odXt6zt8eWbf9Cwt9pPKIQ+ZQHgpJFA==", + "dependencies": { + "@sealcode/file-manager": "^1.0.2", + "@sealcode/ts-predicates": "^0.5.3", + "escape-goat": "^4.0.0", + "koa-responsive-image-router": "^0.2.19", + "locreq": "^3.0.0", + "marked": "^12.0.0", + "mri": "^1.2.0", "tempstream": "^0.3.4", "uuid": "^9.0.1" } @@ -877,29 +893,19 @@ "resolved": "https://registry.npmjs.org/@sealcode/ts-predicates/-/ts-predicates-0.5.3.tgz", "integrity": "sha512-EZI7e8EY8gI1pw2bKdevjl+fBJbcSlpNkCZ8XoEOV3cHakPujiT6M4l775RDkfxJSbLX7jhOBkhgPNDfmCpZbg==" }, - "node_modules/@sealcode/jdd/node_modules/@types/node": { - "version": "14.18.63", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" - }, - "node_modules/@sealcode/jdd/node_modules/locreq": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/locreq/-/locreq-2.4.1.tgz", - "integrity": "sha512-Itfhlus87Q9GcQKEK5LXZngD5YpUbANC9mr5UFghLSBIg6gVVG21AWX45+JVyaqBxILQkB+dBW0i2KlSVzGfeQ==", - "dependencies": { - "@types/node": "^14.14.16" - } - }, "node_modules/@sealcode/sealgen": { - "version": "0.14.16", - "resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.14.16.tgz", - "integrity": "sha512-kWrykFO+LX6qFubetiSXk1fdPtXSFiYpUdX5PT2L/dlfVFX+F+YcPihIwsVucIC/MaYUDR4p5QiHQnt/WXWSFQ==", + "version": "0.15.6", + "resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.15.6.tgz", + "integrity": "sha512-XVmgGwSjzxQXEscvvHiAT6CTcuIV9c4Klb0pDYqNRtXlvZsoJHbhTsp+1Uua9vQh0NRe6Y+V2airw8eLr+ZZpg==", "dependencies": { "@koa/router": "^12.0.1", + "@sealcode/file-manager": "^1.0.2", "@sealcode/ts-predicates": "^0.4.3", "deepmerge": "^4.3.1", "esbuild": "^0.20.0", + "escape-goat": "^4.0.0", "google-fonts-helper": "^3.4.1", + "is-what": "^4.1.16", "js-convert-case": "^4.2.0", "locreq": "^3.0.0", "md5": "^2.3.0", @@ -913,12 +919,12 @@ "yargs": "^17.6.2" }, "bin": { - "sealgen": "dist/cli.js" + "sealgen": "lib/cli.js" }, "peerDependencies": { "koa": "^2.13.0", "koa-responsive-image-router": "^0.2.24", - "sealious": "^0.18.1" + "sealious": "^0.19.7" } }, "node_modules/@sealcode/sealgen/node_modules/@sealcode/ts-predicates": { @@ -1100,9 +1106,9 @@ "integrity": "sha512-miV7Z8zvOnRn0ZjbP/D/qb1VWHrWkKOnfC764SJvnCeIziW4pZy3tPK/542seSgccGAXlPQd/seuNyVAS/p5Ug==" }, "node_modules/@types/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-87W6MJCKZYDhLAx/J1ikW8niMvmGRyY+rpUxWpL1cO7F8Uu5CHuQoFv+R0/L5pgNdW4jTyda42kv60uwVIPjLw==" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-hulKeREDdLFesGQjl96+4aoJSHY5b2GRjagzzcqCfIrWhe5vkCqIvrLbqzBaI1q94Vg8DNJZZqTR5ocdWmWclg==" }, "node_modules/@types/connect": { "version": "3.4.38", @@ -1314,6 +1320,11 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==" + }, "node_modules/@types/mjml": { "version": "4.7.4", "resolved": "https://registry.npmjs.org/@types/mjml/-/mjml-4.7.4.tgz", @@ -1428,8 +1439,7 @@ "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", @@ -2531,9 +2541,9 @@ } }, "node_modules/bson": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.5.0.tgz", - "integrity": "sha512-DXf1BTAS8vKyR90BO4x5v3rKVarmkdkzwOrnYDFdjAY694ILNDkmA3uRh1xXJEl+C1DAh8XCvAQ+Gh3kzubtpg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.6.0.tgz", + "integrity": "sha512-BVINv2SgcMjL4oYbBuCQTpE3/VKOSxrOA8Cj/wQP7izSzlBGVomdm+TcUd0Pzy0ytLSSDweCKQ6X3f5veM5LQA==", "engines": { "node": ">=16.20.1" } @@ -3627,9 +3637,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz", - "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", @@ -3670,11 +3680,11 @@ "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.7", + "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", "which-typed-array": "^1.1.15" }, @@ -3803,11 +3813,11 @@ } }, "node_modules/escape-goat": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", - "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5554,6 +5564,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -5854,9 +5875,9 @@ } }, "node_modules/js-beautify/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6677,17 +6698,14 @@ } }, "node_modules/mime": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.1.tgz", - "integrity": "sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA==", - "funding": [ - "https://github.com/sponsors/broofa" - ], + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "bin": { - "mime": "bin/cli.js" + "mime": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=4.0.0" } }, "node_modules/mime-db": { @@ -6881,9 +6899,9 @@ } }, "node_modules/mjml-cli/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -7315,7 +7333,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, "engines": { "node": ">=4" } @@ -8154,9 +8171,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.1.tgz", + "integrity": "sha512-tS24spDe/zXhWbNPErCHs/AGOzbKGHT+ybSBqmdLm8WZ1xXLWvH8Qn71QPAlqVhd0qUTWjy+Kl9JmISgDdEjsA==", "engines": { "node": "14 || >=16.14" } @@ -9033,11 +9050,12 @@ } }, "node_modules/sealious": { - "version": "0.18.3", - "resolved": "https://registry.npmjs.org/sealious/-/sealious-0.18.3.tgz", - "integrity": "sha512-WxwThRgsTCHxIUVzp6MAK65jBp7lnqPqex8rLondalx62I6E9r2gg0uC1am3/ziZSWgFdpkGKQoiOry0FZyHuw==", + "version": "0.19.7", + "resolved": "https://registry.npmjs.org/sealious/-/sealious-0.19.7.tgz", + "integrity": "sha512-yhKSd+AsFqzCc7UUK4aBIk/YZS7XrvcGJLJyR9m8cVSzN9dmP3glx4xL+gogBOG+SirsDw6FQZz9VKdhshzVUA==", "dependencies": { "@koa/router": "^12.0.1", + "@sealcode/file-manager": "^1.0.1", "@sealcode/ts-predicates": "^0.4.3", "@types/bluebird": "^3.5.30", "@types/boom": "^7.3.0", @@ -9079,7 +9097,6 @@ "koa-send": "^5.0.1", "koa-static": "^5.0.0", "locreq": "^3.0.0", - "mime": "^4.0.1", "mjml": "^4.2.0", "mongodb": "^6.5.0", "nodemailer": "^6.4.6", @@ -10984,6 +11001,17 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/web-resource-inliner/node_modules/escape-goat": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/web-resource-inliner/node_modules/htmlparser2": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", @@ -10998,17 +11026,6 @@ "url": "https://github.com/fb55/htmlparser2?sponsor=1" } }, - "node_modules/web-resource-inliner/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index f75a0cb..a596aa6 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "watch": "multiple-scripts-tmux -p watch", "reset-db": "docker-compose down && docker-compose up -d", "install-test-deps": "npx playwright install firefox", - "pretest-cmd": "npm run build", + "pretest-cmd": "rm -rf dist && npm run build", "test-cmd": "vitest --config ./src/back/vitest.config.ts", "test-cmd-once": "vitest run --config ./src/back/vitest.config.ts", "test": "npm run test-cmd -- --ui", @@ -38,11 +38,13 @@ "@hotwired/turbo": "^8.0.2", "@koa/router": "^12.0.1", "@playwright/test": "^1.36.1", - "@sealcode/jdd": "^0.3.5", - "@sealcode/sealgen": "^0.14.16", + "@sealcode/file-manager": "^1.0.2", + "@sealcode/jdd": "^0.4.5", + "@sealcode/sealgen": "^0.15.6", "@sealcode/ts-predicates": "^0.6.2", "@types/kill-port": "^2.0.0", "@types/leaflet": "^1.9.8", + "escape-goat": "^4.0.0", "get-port": "^7.0.0", "js-convert-case": "^4.2.0", "koa-responsive-image-router": "^0.2.24", @@ -51,7 +53,7 @@ "nodemon": "^3.0.1", "object-path": "^0.11.8", "qs": "^6.12.0", - "sealious": "^0.18.3", + "sealious": "^0.19.6", "stimulus": "^2.0.0", "tempstream": "^0.3.15", "vitest": "^1.1.0" diff --git a/src/back/app.ts b/src/back/app.ts index 7cadc5e..34bb1be 100644 --- a/src/back/app.ts +++ b/src/back/app.ts @@ -11,8 +11,10 @@ import { MONGO_HOST, MONGO_PORT, PORT, + UPLOADS_FS_DIR, } from "./config.js"; import ADMIN_CREDENTIALS from "./default-admin-credentials.js"; +import { TheFileManager } from "./file-manager.js"; import { module_dirname } from "./utils/module_dirname.js"; const locreq = _locreq(module_dirname(import.meta.url)); @@ -25,8 +27,9 @@ declare module "koa" { } export default class TheApp extends App { + FileManager = TheFileManager; config = { - upload_path: locreq.resolve("uploaded_files"), + upload_path: UPLOADS_FS_DIR, datastore_mongo: { host: MONGO_HOST, port: MONGO_PORT, diff --git a/src/back/config.ts b/src/back/config.ts index 3fdde0a..12983d1 100644 --- a/src/back/config.ts +++ b/src/back/config.ts @@ -25,3 +25,6 @@ export const IMAGE_CACHE_FS_DIR = process.env.IMAGE_CACHE_FS_DIR || locreq.resolve("cache/images"); export const SMARTCROP_CACHE_FS_DIR = process.env.IMAGE_CACHE_FS_DIR || locreq.resolve("cache/smartcrop"); + +export const UPLOADS_FS_DIR = + process.env.UPLOADS_FS_DIR || locreq.resolve("uploaded_files"); diff --git a/src/back/file-manager.ts b/src/back/file-manager.ts new file mode 100644 index 0000000..603438a --- /dev/null +++ b/src/back/file-manager.ts @@ -0,0 +1,7 @@ +import { FileManager } from "@sealcode/file-manager"; +import { UPLOADS_FS_DIR } from "./config.js"; + +export const TheFileManager = new FileManager( + UPLOADS_FS_DIR, + "/api/v1/uploaded-files" // this value comes from Sealious, currently not customizable +); diff --git a/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx b/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx index f9140a7..b44e679 100644 --- a/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx +++ b/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx @@ -1,7 +1,7 @@ import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; import type { - ExtractStructuredComponentArgumentsValues, + ExtractStructuredComponentArgumentsParsed, JDDContext, } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; @@ -31,7 +31,7 @@ export class AutoscrollingImages extends Component { interval, imagesPerPage, images, - }: ExtractStructuredComponentArgumentsValues, + }: ExtractStructuredComponentArgumentsParsed, { render_image }: JDDContext ): FlatTemplatable { const imageNumberPerPage = parseInt(imagesPerPage); diff --git a/src/back/jdd-components/faq-component/faq-component.jdd.tsx b/src/back/jdd-components/faq-component/faq-component.jdd.tsx index f41ea9d..dbd02a5 100644 --- a/src/back/jdd-components/faq-component/faq-component.jdd.tsx +++ b/src/back/jdd-components/faq-component/faq-component.jdd.tsx @@ -1,7 +1,7 @@ import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; import type { - ExtractStructuredComponentArgumentsValues, + ExtractStructuredComponentArgumentsParsed, JDDContext, } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; @@ -35,7 +35,7 @@ export class FaqComponent extends Component { content, faq, button, - }: ExtractStructuredComponentArgumentsValues, + }: ExtractStructuredComponentArgumentsParsed, { render_markdown }: JDDContext ): FlatTemplatable { const buttonText = button.buttonText.toUpperCase(); diff --git a/src/back/jdd-components/header-with-image/header-with-image.jdd.tsx b/src/back/jdd-components/header-with-image/header-with-image.jdd.tsx index 7102445..6bd88f0 100644 --- a/src/back/jdd-components/header-with-image/header-with-image.jdd.tsx +++ b/src/back/jdd-components/header-with-image/header-with-image.jdd.tsx @@ -1,7 +1,7 @@ import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; import type { - ExtractStructuredComponentArgumentsValues, + ExtractStructuredComponentArgumentsParsed, JDDContext, } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; @@ -30,7 +30,7 @@ export class HeaderWithImage extends Component { content, image_with_alt, button, - }: ExtractStructuredComponentArgumentsValues, + }: ExtractStructuredComponentArgumentsParsed, { render_markdown, render_image }: JDDContext ): FlatTemplatable { const buttonText = button.text.toUpperCase(); diff --git a/src/back/jdd-components/image-demo/image-demo.jdd.tsx b/src/back/jdd-components/image-demo/image-demo.jdd.tsx index d04b012..03c9c47 100644 --- a/src/back/jdd-components/image-demo/image-demo.jdd.tsx +++ b/src/back/jdd-components/image-demo/image-demo.jdd.tsx @@ -1,7 +1,7 @@ import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; import type { - ExtractStructuredComponentArgumentsValues, + ExtractStructuredComponentArgumentsParsed, JDDContext, } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; @@ -23,7 +23,7 @@ export class ImageDemo extends Component { { image_with_alt, multiple_images, - }: ExtractStructuredComponentArgumentsValues, + }: ExtractStructuredComponentArgumentsParsed, { render_image }: JDDContext ): FlatTemplatable { return ( diff --git a/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx b/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx index c28d968..7894799 100644 --- a/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx +++ b/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx @@ -1,6 +1,6 @@ import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; -import type { ExtractStructuredComponentArgumentsValues } from "@sealcode/jdd"; +import type { ExtractStructuredComponentArgumentsParsed } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; const coordinates = new ComponentArguments.ShortText(); @@ -51,7 +51,7 @@ export class MapWithPins extends Component { toHTML({ pins, - }: ExtractStructuredComponentArgumentsValues< + }: ExtractStructuredComponentArgumentsParsed< typeof component_arguments >): FlatTemplatable { return ( diff --git a/src/back/jdd-components/nice-box/nice-box.jdd.tsx b/src/back/jdd-components/nice-box/nice-box.jdd.tsx index 7b0acb8..1168be3 100644 --- a/src/back/jdd-components/nice-box/nice-box.jdd.tsx +++ b/src/back/jdd-components/nice-box/nice-box.jdd.tsx @@ -1,6 +1,6 @@ import { TempstreamJSX } from "tempstream"; import type { - ExtractStructuredComponentArgumentsValues, + ExtractStructuredComponentArgumentsParsed, JDDContext, } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; @@ -27,7 +27,7 @@ export class NiceBox extends Component { title, content, images, - }: ExtractStructuredComponentArgumentsValues, + }: ExtractStructuredComponentArgumentsParsed, { render_markdown, render_image }: JDDContext ): Promise { return ( diff --git a/src/back/jdd-components/table/table.jdd.tsx b/src/back/jdd-components/table/table.jdd.tsx index f199028..1e7c3a3 100644 --- a/src/back/jdd-components/table/table.jdd.tsx +++ b/src/back/jdd-components/table/table.jdd.tsx @@ -1,6 +1,6 @@ import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; -import type { ExtractStructuredComponentArgumentsValues } from "@sealcode/jdd"; +import type { ExtractStructuredComponentArgumentsParsed } from "@sealcode/jdd"; import { Component, ComponentArguments, isTableHeader } from "@sealcode/jdd"; const component_arguments = { @@ -25,7 +25,7 @@ export class Table extends Component { toHTML({ table, - }: ExtractStructuredComponentArgumentsValues< + }: ExtractStructuredComponentArgumentsParsed< typeof component_arguments >): FlatTemplatable { return ( diff --git a/src/back/jdd-context.ts b/src/back/jdd-context.ts new file mode 100644 index 0000000..120abdc --- /dev/null +++ b/src/back/jdd-context.ts @@ -0,0 +1,16 @@ +import type { JDDContext } from "@sealcode/jdd"; +import type { FilePointer } from "@sealcode/file-manager"; +import { makeSimpleJDDContext } from "@sealcode/jdd"; +import { TheFileManager } from "./file-manager.js"; +import { imageRouter } from "./image-router.js"; + +export const jdd_context: JDDContext = { + ...makeSimpleJDDContext(TheFileManager), + render_image: async (image: string | FilePointer | null, args) => { + if (!image) { + return ""; + } + const file = await TheFileManager.toPointer(image); + return imageRouter.image(await file.getPath(), args); + }, +}; diff --git a/src/back/routes/component-preview/component-input-image.tsx b/src/back/routes/component-preview/component-input-image.tsx index 8ed43e3..930040c 100644 --- a/src/back/routes/component-preview/component-input-image.tsx +++ b/src/back/routes/component-preview/component-input-image.tsx @@ -1,10 +1,12 @@ import type { Image } from "@sealcode/jdd"; import type { StatefulPage } from "@sealcode/sealgen"; import { TempstreamJSX } from "tempstream"; +import { jdd_context } from "../../jdd-context.js"; import type { ComponentPreviewState } from "../components.sreact.js"; -import { jdd_context } from "../jdd-context.js"; import type { ComponentPreviewActions } from "./component-preview-actions.js"; import { printArgPath } from "./print-arg-path.js"; +import { htmlEscape } from "escape-goat"; +import type { FilePointer } from "@sealcode/file-manager"; export function ComponentInputImage({ arg_path, @@ -14,7 +16,7 @@ export function ComponentInputImage({ state: State; arg_path: string[]; arg: Image; - value: string; + value: FilePointer | null; page: StatefulPage; }) { return ( @@ -43,9 +45,7 @@ export function ComponentInputImage({ diff --git a/src/back/routes/component-preview/component-input-list.tsx b/src/back/routes/component-preview/component-input-list.tsx index 7e88da5..4dcc6e3 100644 --- a/src/back/routes/component-preview/component-input-list.tsx +++ b/src/back/routes/component-preview/component-input-list.tsx @@ -1,12 +1,14 @@ -import type { List } from "@sealcode/jdd"; +import type { ComponentArgument, List } from "@sealcode/jdd"; import type { StatefulPage } from "@sealcode/sealgen"; import { TempstreamJSX } from "tempstream"; import type { ComponentPreviewState } from "../components.sreact.js"; -import { jdd_context } from "../jdd-context.js"; import { ComponentInput } from "./component-input.js"; import type { ComponentPreviewActions } from "./component-preview-actions.js"; -export async function ComponentInputList({ +export async function ComponentInputList< + State extends ComponentPreviewState, + T extends ComponentArgument +>({ state, arg_path, arg, @@ -50,8 +52,7 @@ export async function ComponentInputList action: "add_array_item", label: "➕", }, - arg_path, - await arg.item_type.getExampleValue(jdd_context) + arg_path )} ); diff --git a/src/back/routes/component-preview/component-input-table.tsx b/src/back/routes/component-preview/component-input-table.tsx index 53e6215..6f1a99a 100644 --- a/src/back/routes/component-preview/component-input-table.tsx +++ b/src/back/routes/component-preview/component-input-table.tsx @@ -3,7 +3,6 @@ import { isTableHeader } from "@sealcode/jdd"; import type { StatefulPage } from "@sealcode/sealgen"; import { TempstreamJSX } from "tempstream"; import type { ComponentPreviewState } from "../components.sreact.js"; -import { jdd_context } from "../jdd-context.js"; import { ComponentInput } from "./component-input.js"; import type { ComponentPreviewActions } from "./component-preview-actions.js"; @@ -35,8 +34,6 @@ export async function ComponentInputTable< if (!value) { value = arg.getEmptyValue(); } - const empty_cell_value = arg.cell_type.getExampleValue(jdd_context); - const empty_header_value = arg.header_type.getExampleValue(jdd_context); return (
@@ -171,8 +168,7 @@ export async function ComponentInputTable< src="${add_column_right_icon.url}" />`, }, - arg_path, - empty_cell_value + arg_path )} ) : ( @@ -197,7 +193,6 @@ export async function ComponentInputTable< />`, }, arg_path, - empty_cell_value, arg.getColumnsCount(value) )} {page.makeActionButton( @@ -212,7 +207,6 @@ export async function ComponentInputTable< />`, }, arg_path, - empty_header_value, arg.getColumnsCount(value), "header" )} diff --git a/src/back/routes/component-preview/component-input.tsx b/src/back/routes/component-preview/component-input.tsx index 58a914a..af8e1a6 100644 --- a/src/back/routes/component-preview/component-input.tsx +++ b/src/back/routes/component-preview/component-input.tsx @@ -12,6 +12,7 @@ import { ComponentInputTable } from "./component-input-table.js"; import { printArgPath } from "./print-arg-path.js"; import type { ComponentPreviewActions } from "./component-preview-actions.js"; import { is, predicates } from "@sealcode/ts-predicates"; +import type { FilePointer } from "@sealcode/file-manager"; export const actionName = "Components"; const absoluteUrlPattern = "http(s?)(://)((www.)?)(([^.]+).)?([a-zA-z0-9-_]+)"; @@ -70,7 +71,7 @@ export function ComponentInput({ arg_path, arg, // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - value: value as string, + value: value as FilePointer, page, }); } diff --git a/src/back/routes/component-preview/component-preview-actions.ts b/src/back/routes/component-preview/component-preview-actions.ts index 54ec334..d6a4cb7 100644 --- a/src/back/routes/component-preview/component-preview-actions.ts +++ b/src/back/routes/component-preview/component-preview-actions.ts @@ -1,9 +1,9 @@ -import type { TableData } from "@sealcode/jdd"; +import type { ComponentArgument, List, Table, TableData } from "@sealcode/jdd"; import { isTableData, isTableRegularRow } from "@sealcode/jdd"; import objectPath from "object-path"; import { registry } from "../../jdd-components/components.js"; +import { jdd_context } from "../../jdd-context.js"; import type { ComponentPreviewState } from "../components.sreact.js"; -import { jdd_context } from "../jdd-context.js"; function moveElement(array: Array, fromIndex: number, toIndex: number): Array { const element = array.splice(fromIndex, 1)[0]; @@ -12,17 +12,30 @@ function moveElement(array: Array, fromIndex: number, toIndex: number): Ar } export const ComponentPreviewActions = { - add_array_item: ( + add_array_item: async ( state: ComponentPreviewState, _: Record, - arg_path: string[], - empty_value: unknown + arg_path: string[] ) => { const component_args = state.component_args; + const component = registry.get(state.component); + if (!component) { + console.error("unknown component: ", state.component); + return state; + } + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const argument = component.getArgumentAtPath( + arg_path, + state.component_args + ) as List> | null; + if (!argument) { + console.error("Didn't find a list argument at this path", arg_path); + return state; + } objectPath.insert( component_args, arg_path, - empty_value, + await argument.item_type.getExampleValue(jdd_context), // eslint-disable-next-line @typescript-eslint/consistent-type-assertions ((objectPath.get(component_args, arg_path) as unknown[]) || []).length ); @@ -67,22 +80,39 @@ export const ComponentPreviewActions = { component_args: (await component?.getExampleValues(jdd_context)) || {}, }; }, - add_table_row: ( + add_table_row: async ( state: ComponentPreviewState, _: Record, arg_path: string[], - empty_value: unknown, columns: number, type: "header" | "row" = "row" ) => { const component_args = state.component_args; let row; + const component = registry.get(state.component); + if (!component) { + console.error("Unknown component: ", state.component); + return state; + } + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const argument = component.getArgumentAtPath( + arg_path, + state.component_args + ) as Table | null; + if (!argument) { + console.error("Unknown component at path", arg_path); + return state; + } if (type == "header") { - row = { type: "header", header_content: empty_value }; + row = { + type: "header", + header_content: await argument.header_type.getExampleValue(jdd_context), + }; } else { const cells = []; for (let i = 0; i < columns; i++) { - cells.push(empty_value); + // eslint-disable-next-line no-await-in-loop + cells.push(await argument.cell_type.getExampleValue(jdd_context)); } row = { type: "row", cells }; } @@ -100,13 +130,22 @@ export const ComponentPreviewActions = { }; return result; }, - add_table_column: ( + add_table_column: async ( state: ComponentPreviewState, _: Record, - arg_path: string[], - empty_value: unknown + arg_path: string[] ) => { const component_args = state.component_args; + const component = registry.get(state.component); + if (!component) { + console.error("Unknown component: ", state.component); + return state; + } + const argument = component.getArgumentAtPath(arg_path, state.component_args); + if (!argument) { + console.error("Unknown component at path", arg_path); + return state; + } // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const tableData: TableData = objectPath.get( component_args, @@ -119,7 +158,8 @@ export const ComponentPreviewActions = { for (const i in tableData.rows) { const row = tableData.rows[i]; if (isTableRegularRow(row)) { - row.cells.push(empty_value); + // eslint-disable-next-line no-await-in-loop + row.cells.push(await argument.getExampleValue(jdd_context)); } } objectPath.set(component_args, arg_path, tableData); diff --git a/src/back/routes/components.sreact.tsx b/src/back/routes/components.sreact.tsx index 9ef8cf1..3a55e3f 100644 --- a/src/back/routes/components.sreact.tsx +++ b/src/back/routes/components.sreact.tsx @@ -1,14 +1,14 @@ import { render, renderEarlyAssets } from "@sealcode/jdd"; -import { StatefulPage, to_base64 } from "@sealcode/sealgen"; +import { StatefulPage } from "@sealcode/sealgen"; import { hasShape, predicates } from "@sealcode/ts-predicates"; import type { BaseContext } from "koa"; import type { Templatable } from "tempstream"; import { tempstream, TempstreamJSX } from "tempstream"; import html, { defaultHead } from "../html.js"; import { registry } from "../jdd-components/components.js"; +import { jdd_context } from "../jdd-context.js"; import { ComponentInput } from "./component-preview/component-input.js"; import { ComponentPreviewActions } from "./component-preview/component-preview-actions.js"; -import { jdd_context } from "./jdd-context.js"; export const actionName = "Components"; @@ -33,6 +33,43 @@ export default new (class ComponentsPage extends StatefulPage< return initial_state; } + async serializeState(_context: BaseContext, state: ComponentPreviewState) { + const component = registry.get(state.component); + const result = JSON.stringify({ + ...state, + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + component_args: component + ? await component.convertParsedToStorage( + jdd_context, + state.component_args + ) + : {}, + }); + return result; + } + + async deserializeState(_context: BaseContext, state_string: string) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const raw = JSON.parse(state_string); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-member-access + const component = registry.get(raw.component as string); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const result = { + ...raw, + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + component_args: component + ? // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access + await component.convertStorageToParsed( + jdd_context, + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access + raw.component_args || {} + ) + : {}, + }; + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return result as ComponentPreviewState; + } + wrapInLayout( ctx: BaseContext, content: Templatable, @@ -62,20 +99,6 @@ export default new (class ComponentsPage extends StatefulPage< ); } - wrapInForm(state: ComponentPreviewState, content: Templatable): Templatable { - // overwriting this method in order to add enctype to form - return ( -
- - {content} -
- ); - } - async preprocessOverrides( state: ComponentPreviewState, overrides: Record @@ -95,11 +118,7 @@ export default new (class ComponentsPage extends StatefulPage< async ([arg_name, arg]) => { const value = overrides.component_args[arg_name]; if (value) { - const new_value = await arg.parseFormInput( - jdd_context, - value, - arg_name - ); + const new_value = await arg.receivedToParsed(jdd_context, value); overrides.component_args[arg_name] = new_value; } } @@ -111,7 +130,7 @@ export default new (class ComponentsPage extends StatefulPage< containerSizes = ["320", "600", "800", "1024", "1300", "1920"]; - render(_ctx: BaseContext, state: ComponentPreviewState) { + render(ctx: BaseContext, state: ComponentPreviewState) { const all_components = registry.getAll(); const component = registry.get(state.component) || Object.values(all_components)[0]; @@ -158,7 +177,7 @@ export default new (class ComponentsPage extends StatefulPage< )}
- {JSON.stringify(state)} + {this.serializeState(ctx, state)}
diff --git a/src/back/routes/jdd-context.ts b/src/back/routes/jdd-context.ts deleted file mode 100644 index 7282138..0000000 --- a/src/back/routes/jdd-context.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { JDDContext } from "@sealcode/jdd"; -import { simpleJDDContext } from "@sealcode/jdd"; -import { imageRouter } from "../image-router.js"; - -export const jdd_context: JDDContext = { - ...simpleJDDContext, - render_image: async (image_id, args) => { - if (!image_id) { - return ""; - } - const image_pointer = await simpleJDDContext.decode_file(image_id); - if (!image_pointer) { - return ""; - } - return imageRouter.image(image_pointer.path, args); - }, -};