From 6070394ccaf304a5c72c2c7233fc90cee1692082 Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 22 Jul 2025 05:50:44 -0500 Subject: [PATCH 01/20] Updated bun.lock file --- bun.lock | 139 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 84 insertions(+), 55 deletions(-) diff --git a/bun.lock b/bun.lock index 7f982e0..ec497f3 100644 --- a/bun.lock +++ b/bun.lock @@ -15,7 +15,7 @@ }, "app": { "name": "bknd", - "version": "0.15.0-rc.10", + "version": "0.15.0", "bin": "./dist/cli/index.js", "dependencies": { "@cfworker/json-schema": "^4.1.1", @@ -124,6 +124,7 @@ "version": "0.5.1", "devDependencies": { "@types/bun": "latest", + "bknd": "workspace:*", "tsdx": "^0.14.1", "typescript": "^5.0.0", }, @@ -1219,7 +1220,7 @@ "@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="], - "@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="], + "@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="], "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], @@ -1751,7 +1752,7 @@ "cross-port-killer": ["cross-port-killer@1.4.0", "", { "bin": { "kill-port": "source/cli.js" } }, "sha512-ujqfftKsSeorFMVI6JP25xMBixHEaDWVK+NarRZAGnJjR5AhebRQU+g+k/Lj8OHwM6f+wrrs8u5kkCdI7RLtxQ=="], - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="], "css-box-model": ["css-box-model@1.2.1", "", { "dependencies": { "tiny-invariant": "^1.0.6" } }, "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw=="], @@ -2851,7 +2852,7 @@ "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], @@ -2885,7 +2886,7 @@ "pg-protocol": ["pg-protocol@1.8.0", "", {}, "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g=="], - "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], + "pg-types": ["pg-types@4.0.2", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng=="], "pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="], @@ -2939,13 +2940,13 @@ "postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="], - "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], + "postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="], - "postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="], + "postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="], - "postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], + "postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="], - "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + "postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="], "postgres-range": ["postgres-range@1.1.4", "", {}, "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w=="], @@ -3027,7 +3028,7 @@ "react-icons": ["react-icons@5.2.1", "", { "peerDependencies": { "react": "*" } }, "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw=="], - "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], "react-json-view-lite": ["react-json-view-lite@2.4.1", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-fwFYknRIBxjbFm0kBDrzgBy1xa5tDg2LyXXBepC5f1b+MY3BUClMCsvanMPn089JbV1Eg3nZcrp0VCuH43aXnA=="], @@ -3209,9 +3210,9 @@ "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + "shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "^1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="], - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], "shelljs": ["shelljs@0.8.5", "", { "dependencies": { "glob": "^7.0.0", "interpret": "^1.0.0", "rechoir": "^0.6.2" }, "bin": { "shjs": "bin/shjs" } }, "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow=="], @@ -3637,7 +3638,7 @@ "whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], @@ -3877,6 +3878,8 @@ "@neondatabase/serverless/@types/pg": ["@types/pg@8.6.6", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw=="], + "@plasmicapp/nextjs-app-router/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "@plasmicapp/query/swr": ["swr@1.3.0", "", { "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw=="], "@puppeteer/browsers/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], @@ -3893,6 +3896,8 @@ "@remix-run/web-fetch/mrmime": ["mrmime@1.0.1", "", {}, "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw=="], + "@rjsf/utils/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "@rollup/plugin-commonjs/magic-string": ["magic-string@0.25.9", "", { "dependencies": { "sourcemap-codec": "^1.4.8" } }, "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="], "@rollup/plugin-replace/magic-string": ["magic-string@0.25.9", "", { "dependencies": { "sourcemap-codec": "^1.4.8" } }, "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="], @@ -4015,9 +4020,7 @@ "@testing-library/jest-dom/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], - "@types/bun/bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="], - - "@types/pg/pg-types": ["pg-types@4.0.2", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng=="], + "@types/bun/bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="], "@typescript-eslint/experimental-utils/eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="], @@ -4133,12 +4136,16 @@ "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "cross-spawn/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + "degenerator/escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="], "domexception/webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="], "duplexify/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "edge-paths/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "edgedriver/fast-xml-parser": ["fast-xml-parser@4.5.3", "", { "dependencies": { "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig=="], "edgedriver/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], @@ -4157,8 +4164,6 @@ "eslint/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], - "eslint/cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="], - "eslint/globals": ["globals@12.4.0", "", { "dependencies": { "type-fest": "^0.8.1" } }, "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg=="], "eslint/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], @@ -4201,6 +4206,8 @@ "espree/acorn": ["acorn@7.4.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="], + "execa/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "expand-brackets/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "expand-brackets/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "^0.1.0" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="], @@ -4225,6 +4232,8 @@ "flat-cache/rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="], + "foreground-child/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -4281,6 +4290,8 @@ "jest-haste-map/jest-worker": ["jest-worker@25.5.0", "", { "dependencies": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw=="], + "jest-haste-map/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "jest-jasmine2/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], "jest-matcher-utils/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], @@ -4361,10 +4372,10 @@ "node-notifier/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "node-notifier/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], - "normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + "npm-run-path/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "object-copy/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "^0.1.0" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="], "object-copy/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], @@ -4379,9 +4390,9 @@ "peek-stream/duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="], - "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + "pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], - "pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], "progress-estimator/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], @@ -4389,8 +4400,6 @@ "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], - "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "pumpify/duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="], "pumpify/pump": ["pump@2.0.1", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA=="], @@ -4625,18 +4634,18 @@ "@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "@neondatabase/serverless/@types/pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], + + "@plasmicapp/nextjs-app-router/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "@plasmicapp/nextjs-app-router/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "@plasmicapp/nextjs-app-router/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "@testing-library/dom/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], "@testing-library/dom/pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], - "@types/pg/pg-types/postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="], - - "@types/pg/pg-types/postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="], - - "@types/pg/pg-types/postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="], - - "@types/pg/pg-types/postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="], - "@verdaccio/local-storage-legacy/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="], "@verdaccio/logger/pino/on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="], @@ -4703,14 +4712,6 @@ "eslint/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], - "eslint/cross-spawn/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], - - "eslint/cross-spawn/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - - "eslint/cross-spawn/shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "^1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="], - - "eslint/cross-spawn/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], - "eslint/globals/type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="], "eslint/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], @@ -4719,6 +4720,12 @@ "eslint/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="], + "execa/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "execa/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "execa/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "expand-brackets/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "expand-brackets/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="], @@ -4733,6 +4740,12 @@ "find-cache-dir/make-dir/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "foreground-child/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "foreground-child/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "foreground-child/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "geckodriver/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], "glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], @@ -4741,6 +4754,8 @@ "has-values/is-number/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + "jest-changed-files/execa/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "jest-cli/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="], "jest-cli/yargs/decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="], @@ -4809,6 +4824,14 @@ "peek-stream/duplexify/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "pg/pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], + + "pg/pg-types/postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="], + + "pg/pg-types/postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], + + "pg/pg-types/postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + "progress-estimator/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], "progress-estimator/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], @@ -4825,8 +4848,6 @@ "sane/anymatch/normalize-path": ["normalize-path@2.1.1", "", { "dependencies": { "remove-trailing-separator": "^1.0.1" } }, "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w=="], - "sane/execa/cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="], - "sane/execa/get-stream": ["get-stream@4.1.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w=="], "sane/execa/is-stream": ["is-stream@1.1.0", "", {}, "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="], @@ -4975,6 +4996,16 @@ "@cloudflare/vitest-pool-workers/miniflare/youch/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + "@neondatabase/serverless/@types/pg/pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], + + "@neondatabase/serverless/@types/pg/pg-types/postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="], + + "@neondatabase/serverless/@types/pg/pg-types/postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], + + "@neondatabase/serverless/@types/pg/pg-types/postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + + "@plasmicapp/nextjs-app-router/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "@verdaccio/middleware/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "@vitest/coverage-v8/vitest/vite/rollup": ["rollup@4.35.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.35.0", "@rollup/rollup-android-arm64": "4.35.0", "@rollup/rollup-darwin-arm64": "4.35.0", "@rollup/rollup-darwin-x64": "4.35.0", "@rollup/rollup-freebsd-arm64": "4.35.0", "@rollup/rollup-freebsd-x64": "4.35.0", "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", "@rollup/rollup-linux-arm-musleabihf": "4.35.0", "@rollup/rollup-linux-arm64-gnu": "4.35.0", "@rollup/rollup-linux-arm64-musl": "4.35.0", "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", "@rollup/rollup-linux-riscv64-gnu": "4.35.0", "@rollup/rollup-linux-s390x-gnu": "4.35.0", "@rollup/rollup-linux-x64-gnu": "4.35.0", "@rollup/rollup-linux-x64-musl": "4.35.0", "@rollup/rollup-win32-arm64-msvc": "4.35.0", "@rollup/rollup-win32-ia32-msvc": "4.35.0", "@rollup/rollup-win32-x64-msvc": "4.35.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg=="], @@ -5001,7 +5032,15 @@ "eslint/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], - "eslint/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], + "execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "foreground-child/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "jest-changed-files/execa/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "jest-changed-files/execa/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "jest-changed-files/execa/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "jest-cli/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], @@ -5033,16 +5072,6 @@ "rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], - "sane/execa/cross-spawn/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], - - "sane/execa/cross-spawn/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - - "sane/execa/cross-spawn/shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "^1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="], - - "sane/execa/cross-spawn/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], - - "sane/execa/npm-run-path/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], - "sane/micromatch/braces/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], "sane/micromatch/braces/fill-range": ["fill-range@4.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", "repeat-string": "^1.6.1", "to-regex-range": "^2.1.0" } }, "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ=="], @@ -5059,14 +5088,14 @@ "eslint/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + "jest-changed-files/execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "log-symbols/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], "log-update/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@1.2.0", "", {}, "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="], "progress-estimator/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], - "sane/execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], - "sane/micromatch/braces/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], "sane/micromatch/braces/fill-range/is-number": ["is-number@3.0.0", "", { "dependencies": { "kind-of": "^3.0.2" } }, "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg=="], From c833b2b5399296b5a4a300bcfa3773605930f056 Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 22 Jul 2025 05:53:23 -0500 Subject: [PATCH 02/20] feat: add @clack/prompts because somehow it was missing? --- app/package.json | 1 + bun.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/app/package.json b/app/package.json index a1ced70..4bdd5fa 100644 --- a/app/package.json +++ b/app/package.json @@ -47,6 +47,7 @@ "license": "FSL-1.1-MIT", "dependencies": { "@cfworker/json-schema": "^4.1.1", + "@clack/prompts": "^0.11.0", "@codemirror/lang-html": "^6.4.9", "@codemirror/lang-json": "^6.0.1", "@hello-pangea/dnd": "^18.0.1", diff --git a/bun.lock b/bun.lock index ec497f3..bf7e37b 100644 --- a/bun.lock +++ b/bun.lock @@ -19,6 +19,7 @@ "bin": "./dist/cli/index.js", "dependencies": { "@cfworker/json-schema": "^4.1.1", + "@clack/prompts": "^0.11.0", "@codemirror/lang-html": "^6.4.9", "@codemirror/lang-json": "^6.0.1", "@hello-pangea/dnd": "^18.0.1", @@ -504,6 +505,10 @@ "@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="], + "@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="], + + "@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="], + "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="], "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.2", "", { "peerDependencies": { "unenv": "2.0.0-rc.17", "workerd": "^1.20250508.0" }, "optionalPeers": ["workerd"] }, "sha512-MtUgNl+QkQyhQvv5bbWP+BpBC1N0me4CHHuP2H4ktmOMKdB/6kkz/lo+zqiA4mEazb4y+1cwyNjVrQ2DWeE4mg=="], From f07f3d10075ccdccedcd922e519094e91f586c1e Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 22 Jul 2025 08:28:23 -0500 Subject: [PATCH 03/20] refactor: rename basepath to admin_basepath and normalize admin paths --- app/src/ui/client/BkndProvider.tsx | 4 ++-- app/src/ui/client/utils/AppReduced.ts | 26 +++++++++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/app/src/ui/client/BkndProvider.tsx b/app/src/ui/client/BkndProvider.tsx index 4e938d1..113ec73 100644 --- a/app/src/ui/client/BkndProvider.tsx +++ b/app/src/ui/client/BkndProvider.tsx @@ -9,7 +9,7 @@ import { Message } from "ui/components/display/Message"; import { useNavigate } from "ui/lib/routes"; export type BkndAdminOptions = { - basepath?: string; + admin_basepath?: string; logo_return_path?: string; theme?: AppTheme; }; @@ -169,7 +169,7 @@ export function useBkndOptions(): BkndAdminOptions { const ctx = useContext(BkndContext); return ( ctx.options ?? { - basepath: "/", + admin_basepath: "/", } ); } diff --git a/app/src/ui/client/utils/AppReduced.ts b/app/src/ui/client/utils/AppReduced.ts index 4da7b0d..f7eb603 100644 --- a/app/src/ui/client/utils/AppReduced.ts +++ b/app/src/ui/client/utils/AppReduced.ts @@ -6,6 +6,23 @@ import type { BkndAdminOptions } from "ui/client/BkndProvider"; export type AppType = ReturnType; +/** + * Normalize admin path by removing duplicate slashes and ensuring proper format + * @param path - The path to normalize + * @returns Normalized path + * @private + */ +function normalizeAdminPath(path: string): string { + // Remove duplicate slashes + const normalized = path.replace(/\/+/g, "/"); + // Don't remove trailing slash if it's the only character or if path ends with entity/ + if (normalized === "/" || normalized.endsWith("/entity/")) { + return normalized; + } + // Remove trailing slash for other paths + return normalized.replace(/\/$/, "") || "/"; +} + /** * Reduced version of the App class for frontend use * @todo: remove this class @@ -68,20 +85,19 @@ export class AppReduced { get options() { return { - basepath: "", + admin_basepath: "", logo_return_path: "/", ...this._options, }; } getSettingsPath(path: string[] = []): string { - const base = `~/${this.options.basepath}/settings`.replace(/\/+/g, "/"); - return [base, ...path].join("/"); + const base = `~/${this.options.admin_basepath}/settings` + return normalizeAdminPath([base, ...path].join("/")); } getAbsolutePath(path?: string): string { - const { basepath } = this.options; - return (path ? `~/${basepath}/${path}` : `~/${basepath}`).replace(/\/+/g, "/"); + return normalizeAdminPath((path ? `~/${this.options.admin_basepath}/${path}` : `~/${this.options.admin_basepath}`)); } getAuthConfig() { From 03aac3437b0a9d5f4734abbfe50d30d198dd3b7c Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 22 Jul 2025 08:33:42 -0500 Subject: [PATCH 04/20] refactor: unify theme type and admin options from AdminController --- app/src/modules/server/AdminController.tsx | 3 ++- app/src/ui/client/BkndProvider.tsx | 8 ++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/src/modules/server/AdminController.tsx b/app/src/modules/server/AdminController.tsx index 5cb66d6..59fcda3 100644 --- a/app/src/modules/server/AdminController.tsx +++ b/app/src/modules/server/AdminController.tsx @@ -10,6 +10,7 @@ import { css, Style } from "hono/css"; import { Controller } from "modules/Controller"; import * as SystemPermissions from "modules/permissions"; import type { TApiUser } from "Api"; +import type { AppTheme } from "ui/client/use-theme"; const htmlBkndContextReplace = ""; @@ -18,7 +19,7 @@ export type AdminBkndWindowContext = { logout_route: string; admin_basepath: string; logo_return_path?: string; - theme?: "dark" | "light" | "system"; + theme?: AppTheme; }; // @todo: add migration to remove admin path from config diff --git a/app/src/ui/client/BkndProvider.tsx b/app/src/ui/client/BkndProvider.tsx index 113ec73..964d82a 100644 --- a/app/src/ui/client/BkndProvider.tsx +++ b/app/src/ui/client/BkndProvider.tsx @@ -4,15 +4,11 @@ import { createContext, startTransition, useContext, useEffect, useRef, useState import { useApi } from "ui/client"; import { type TSchemaActions, getSchemaActions } from "./schema/actions"; import { AppReduced } from "./utils/AppReduced"; -import type { AppTheme } from "ui/client/use-theme"; import { Message } from "ui/components/display/Message"; import { useNavigate } from "ui/lib/routes"; +import type { AdminBkndWindowContext } from "modules/server/AdminController"; -export type BkndAdminOptions = { - admin_basepath?: string; - logo_return_path?: string; - theme?: AppTheme; -}; +export type BkndAdminOptions = Omit type BkndContext = { version: number; schema: ModuleSchemas; From f0551f50d1fe14b445cdd46808fd1e998e03cdf7 Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 22 Jul 2025 08:36:38 -0500 Subject: [PATCH 05/20] fix: set default admin_basepath and logo_return_path in constructor Move default option values into the constructor parameter initializer to prevent them from being overwritten by the spread operator in `get options()`. --- app/src/ui/client/utils/AppReduced.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/ui/client/utils/AppReduced.ts b/app/src/ui/client/utils/AppReduced.ts index f7eb603..66561c9 100644 --- a/app/src/ui/client/utils/AppReduced.ts +++ b/app/src/ui/client/utils/AppReduced.ts @@ -35,7 +35,10 @@ export class AppReduced { constructor( protected appJson: AppType, - protected _options: BkndAdminOptions = {}, + protected _options: BkndAdminOptions = { + admin_basepath: '', + logo_return_path: '/' + }, ) { //console.log("received appjson", appJson); @@ -85,7 +88,6 @@ export class AppReduced { get options() { return { - admin_basepath: "", logo_return_path: "/", ...this._options, }; From 75c29ae5e22f02d3b6b2ecc1961771fb075337ff Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 22 Jul 2025 08:37:31 -0500 Subject: [PATCH 06/20] Add .env.local to app/.gitignore --- app/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/.gitignore b/app/.gitignore index 8415a0f..c7044f8 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,4 +1,5 @@ playwright-report test-results bknd.config.* -__test__/helper.d.ts \ No newline at end of file +__test__/helper.d.ts +.env.local \ No newline at end of file From 12ef72d595038d0ea46a6a0226bbd912f8fae404 Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 22 Jul 2025 08:52:32 -0500 Subject: [PATCH 07/20] test: add comprehensive tests for AppReduced utility and path normalization --- .../ui/client/utils/AppReduced.spec.ts | 231 ++++++++++++++++++ app/src/ui/client/BkndProvider.tsx | 4 +- app/src/ui/client/utils/AppReduced.ts | 3 +- 3 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 app/__test__/ui/client/utils/AppReduced.spec.ts diff --git a/app/__test__/ui/client/utils/AppReduced.spec.ts b/app/__test__/ui/client/utils/AppReduced.spec.ts new file mode 100644 index 0000000..355170e --- /dev/null +++ b/app/__test__/ui/client/utils/AppReduced.spec.ts @@ -0,0 +1,231 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { AppReduced, type AppType } from 'ui/client/utils/AppReduced'; +import type { BkndAdminOptions } from 'ui/client/BkndProvider'; + +// Import the normalizeAdminPath function for testing +// Note: This assumes the function is exported or we need to test it indirectly through public methods + +describe('AppReduced', () => { + let mockAppJson: AppType; + let appReduced: AppReduced; + + beforeEach(() => { + mockAppJson = { + data: { + entities: {}, + relations: {} + }, + flows: { + flows: {} + }, + auth: {} + } as AppType; + }); + + describe('getSettingsPath', () => { + it('should return settings path with admin_basepath', () => { + const options: BkndAdminOptions = { + admin_basepath: '/admin', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getSettingsPath(); + + expect(result).toBe('~/admin/settings'); + }); + + it('should return settings path with empty admin_basepath', () => { + const options: BkndAdminOptions = { + admin_basepath: '', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getSettingsPath(); + + expect(result).toBe('~/settings'); + }); + + it('should append additional path segments', () => { + const options: BkndAdminOptions = { + admin_basepath: '/admin', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getSettingsPath(['user', 'profile']); + + expect(result).toBe('~/admin/settings/user/profile'); + }); + + it('should normalize multiple slashes', () => { + const options: BkndAdminOptions = { + admin_basepath: '//admin//', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getSettingsPath(['//user//']); + + expect(result).toBe('~/admin/settings/user'); + }); + + it('should handle admin_basepath without leading slash', () => { + const options: BkndAdminOptions = { + admin_basepath: 'admin', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getSettingsPath(); + + expect(result).toBe('~/admin/settings'); + }); + }); + + describe('getAbsolutePath', () => { + it('should return absolute path with admin_basepath', () => { + const options: BkndAdminOptions = { + admin_basepath: '/admin', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getAbsolutePath('dashboard'); + + expect(result).toBe('~/admin/dashboard'); + }); + + it('should return base path when no path provided', () => { + const options: BkndAdminOptions = { + admin_basepath: '/admin', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getAbsolutePath(); + + expect(result).toBe('~/admin'); + }); + + it('should normalize paths correctly', () => { + const options: BkndAdminOptions = { + admin_basepath: '//admin//', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getAbsolutePath('//dashboard//'); + + expect(result).toBe('~/admin/dashboard'); + }); + }); + + describe('options getter', () => { + it('should return merged options with defaults', () => { + const customOptions: BkndAdminOptions = { + admin_basepath: '/custom-admin', + logo_return_path: '/custom-home' + }; + + appReduced = new AppReduced(mockAppJson, customOptions); + const options = appReduced.options; + + expect(options).toEqual({ + logo_return_path: '/custom-home', + admin_basepath: '/custom-admin' + }); + }); + + it('should use default logo_return_path when not provided', () => { + const customOptions: BkndAdminOptions = { + admin_basepath: '/admin' + }; + + appReduced = new AppReduced(mockAppJson, customOptions); + const options = appReduced.options; + + expect(options.logo_return_path).toBe('/'); + expect(options.admin_basepath).toBe('/admin'); + }); + }); + + describe('path normalization behavior', () => { + it('should normalize duplicate slashes in settings path', () => { + const options: BkndAdminOptions = { + admin_basepath: '/admin', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getSettingsPath(['//nested//path//']); + + expect(result).toBe('~/admin/settings/nested/path'); + }); + + it('should handle root path normalization', () => { + const options: BkndAdminOptions = { + admin_basepath: '/', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getAbsolutePath(); + + // The normalizeAdminPath function removes trailing slashes except for root "/" + // When admin_basepath is "/", the result is "~/" which becomes "~" after normalization + expect(result).toBe('~'); + }); + + it('should preserve entity paths ending with slash', () => { + const options: BkndAdminOptions = { + admin_basepath: '/admin', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getAbsolutePath('entity/'); + + expect(result).toBe('~/admin/entity/'); + }); + + it('should remove trailing slashes from non-entity paths', () => { + const options: BkndAdminOptions = { + admin_basepath: '/admin', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getAbsolutePath('dashboard/'); + + expect(result).toBe('~/admin/dashboard'); + }); + }); + + describe('edge cases', () => { + it('should handle undefined admin_basepath', () => { + const options: BkndAdminOptions = { + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getSettingsPath(); + + // When admin_basepath is undefined, it defaults to empty string + expect(result).toBe('~/settings'); + }); + + it('should handle null path segments', () => { + const options: BkndAdminOptions = { + admin_basepath: '/admin', + logo_return_path: '/' + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getSettingsPath(['', 'valid', '']); + + expect(result).toBe('~/admin/settings/valid'); + }); + }); +}); \ No newline at end of file diff --git a/app/src/ui/client/BkndProvider.tsx b/app/src/ui/client/BkndProvider.tsx index 964d82a..a6e09ff 100644 --- a/app/src/ui/client/BkndProvider.tsx +++ b/app/src/ui/client/BkndProvider.tsx @@ -8,7 +8,9 @@ import { Message } from "ui/components/display/Message"; import { useNavigate } from "ui/lib/routes"; import type { AdminBkndWindowContext } from "modules/server/AdminController"; -export type BkndAdminOptions = Omit +export type BkndAdminOptions = Omit & { + admin_basepath?: string; +} type BkndContext = { version: number; schema: ModuleSchemas; diff --git a/app/src/ui/client/utils/AppReduced.ts b/app/src/ui/client/utils/AppReduced.ts index 66561c9..e69f542 100644 --- a/app/src/ui/client/utils/AppReduced.ts +++ b/app/src/ui/client/utils/AppReduced.ts @@ -88,7 +88,8 @@ export class AppReduced { get options() { return { - logo_return_path: "/", + admin_basepath: '', + logo_return_path: '/', ...this._options, }; } From 9e316a92f63ed6990ffdff62c8347bbdf95e8d66 Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 22 Jul 2025 08:57:06 -0500 Subject: [PATCH 08/20] refactor: use AppTheme type and handle empty admin_basepath Replace literal theme union with AppTheme type in AdminController options and make path helpers resilient when admin_basepath is empty. --- app/src/modules/server/AdminController.tsx | 2 +- app/src/ui/client/utils/AppReduced.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/modules/server/AdminController.tsx b/app/src/modules/server/AdminController.tsx index 59fcda3..2583a76 100644 --- a/app/src/modules/server/AdminController.tsx +++ b/app/src/modules/server/AdminController.tsx @@ -30,7 +30,7 @@ export type AdminControllerOptions = { html?: string; forceDev?: boolean | { mainPath: string }; debugRerenders?: boolean; - theme?: "dark" | "light" | "system"; + theme?: AppTheme; logoReturnPath?: string; }; diff --git a/app/src/ui/client/utils/AppReduced.ts b/app/src/ui/client/utils/AppReduced.ts index e69f542..2b703d2 100644 --- a/app/src/ui/client/utils/AppReduced.ts +++ b/app/src/ui/client/utils/AppReduced.ts @@ -95,12 +95,14 @@ export class AppReduced { } getSettingsPath(path: string[] = []): string { - const base = `~/${this.options.admin_basepath}/settings` + const basePath = this.options.admin_basepath ? `~/${this.options.admin_basepath}` : '~'; + const base = `${basePath}/settings` return normalizeAdminPath([base, ...path].join("/")); } getAbsolutePath(path?: string): string { - return normalizeAdminPath((path ? `~/${this.options.admin_basepath}/${path}` : `~/${this.options.admin_basepath}`)); + const basePath = this.options.admin_basepath ? `~/${this.options.admin_basepath}` : '~'; + return normalizeAdminPath(path ? `${basePath}/${path}` : basePath); } getAuthConfig() { From 7b86611f358dcc4af55e63332cade62e91c135f1 Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 22 Jul 2025 08:58:30 -0500 Subject: [PATCH 09/20] bun run format --- .../ui/client/utils/AppReduced.spec.ts | 234 +++++++++--------- app/src/ui/client/BkndProvider.tsx | 7 +- app/src/ui/client/utils/AppReduced.ts | 14 +- app/src/ui/main.css | 2 +- app/tsconfig.build.json | 18 +- 5 files changed, 139 insertions(+), 136 deletions(-) diff --git a/app/__test__/ui/client/utils/AppReduced.spec.ts b/app/__test__/ui/client/utils/AppReduced.spec.ts index 355170e..ca80c2e 100644 --- a/app/__test__/ui/client/utils/AppReduced.spec.ts +++ b/app/__test__/ui/client/utils/AppReduced.spec.ts @@ -1,11 +1,11 @@ -import { describe, it, expect, beforeEach } from 'vitest'; -import { AppReduced, type AppType } from 'ui/client/utils/AppReduced'; -import type { BkndAdminOptions } from 'ui/client/BkndProvider'; +import { describe, it, expect, beforeEach } from "vitest"; +import { AppReduced, type AppType } from "ui/client/utils/AppReduced"; +import type { BkndAdminOptions } from "ui/client/BkndProvider"; // Import the normalizeAdminPath function for testing // Note: This assumes the function is exported or we need to test it indirectly through public methods -describe('AppReduced', () => { +describe("AppReduced", () => { let mockAppJson: AppType; let appReduced: AppReduced; @@ -13,219 +13,219 @@ describe('AppReduced', () => { mockAppJson = { data: { entities: {}, - relations: {} + relations: {}, }, flows: { - flows: {} + flows: {}, }, - auth: {} + auth: {}, } as AppType; }); - describe('getSettingsPath', () => { - it('should return settings path with admin_basepath', () => { + describe("getSettingsPath", () => { + it("should return settings path with admin_basepath", () => { const options: BkndAdminOptions = { - admin_basepath: '/admin', - logo_return_path: '/' + admin_basepath: "/admin", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); const result = appReduced.getSettingsPath(); - - expect(result).toBe('~/admin/settings'); + + expect(result).toBe("~/admin/settings"); }); - it('should return settings path with empty admin_basepath', () => { + it("should return settings path with empty admin_basepath", () => { const options: BkndAdminOptions = { - admin_basepath: '', - logo_return_path: '/' + admin_basepath: "", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); const result = appReduced.getSettingsPath(); - - expect(result).toBe('~/settings'); + + expect(result).toBe("~/settings"); }); - it('should append additional path segments', () => { + it("should append additional path segments", () => { const options: BkndAdminOptions = { - admin_basepath: '/admin', - logo_return_path: '/' + admin_basepath: "/admin", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); - const result = appReduced.getSettingsPath(['user', 'profile']); - - expect(result).toBe('~/admin/settings/user/profile'); + const result = appReduced.getSettingsPath(["user", "profile"]); + + expect(result).toBe("~/admin/settings/user/profile"); }); - it('should normalize multiple slashes', () => { + it("should normalize multiple slashes", () => { const options: BkndAdminOptions = { - admin_basepath: '//admin//', - logo_return_path: '/' + admin_basepath: "//admin//", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); - const result = appReduced.getSettingsPath(['//user//']); - - expect(result).toBe('~/admin/settings/user'); + const result = appReduced.getSettingsPath(["//user//"]); + + expect(result).toBe("~/admin/settings/user"); }); - it('should handle admin_basepath without leading slash', () => { + it("should handle admin_basepath without leading slash", () => { const options: BkndAdminOptions = { - admin_basepath: 'admin', - logo_return_path: '/' + admin_basepath: "admin", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); const result = appReduced.getSettingsPath(); - - expect(result).toBe('~/admin/settings'); + + expect(result).toBe("~/admin/settings"); }); }); - describe('getAbsolutePath', () => { - it('should return absolute path with admin_basepath', () => { + describe("getAbsolutePath", () => { + it("should return absolute path with admin_basepath", () => { const options: BkndAdminOptions = { - admin_basepath: '/admin', - logo_return_path: '/' + admin_basepath: "/admin", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); - const result = appReduced.getAbsolutePath('dashboard'); - - expect(result).toBe('~/admin/dashboard'); + const result = appReduced.getAbsolutePath("dashboard"); + + expect(result).toBe("~/admin/dashboard"); }); - it('should return base path when no path provided', () => { + it("should return base path when no path provided", () => { const options: BkndAdminOptions = { - admin_basepath: '/admin', - logo_return_path: '/' + admin_basepath: "/admin", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); const result = appReduced.getAbsolutePath(); - - expect(result).toBe('~/admin'); + + expect(result).toBe("~/admin"); }); - it('should normalize paths correctly', () => { + it("should normalize paths correctly", () => { const options: BkndAdminOptions = { - admin_basepath: '//admin//', - logo_return_path: '/' + admin_basepath: "//admin//", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); - const result = appReduced.getAbsolutePath('//dashboard//'); - - expect(result).toBe('~/admin/dashboard'); + const result = appReduced.getAbsolutePath("//dashboard//"); + + expect(result).toBe("~/admin/dashboard"); }); }); - describe('options getter', () => { - it('should return merged options with defaults', () => { + describe("options getter", () => { + it("should return merged options with defaults", () => { const customOptions: BkndAdminOptions = { - admin_basepath: '/custom-admin', - logo_return_path: '/custom-home' + admin_basepath: "/custom-admin", + logo_return_path: "/custom-home", }; - + appReduced = new AppReduced(mockAppJson, customOptions); const options = appReduced.options; - + expect(options).toEqual({ - logo_return_path: '/custom-home', - admin_basepath: '/custom-admin' + logo_return_path: "/custom-home", + admin_basepath: "/custom-admin", }); }); - it('should use default logo_return_path when not provided', () => { + it("should use default logo_return_path when not provided", () => { const customOptions: BkndAdminOptions = { - admin_basepath: '/admin' + admin_basepath: "/admin", }; - + appReduced = new AppReduced(mockAppJson, customOptions); const options = appReduced.options; - - expect(options.logo_return_path).toBe('/'); - expect(options.admin_basepath).toBe('/admin'); + + expect(options.logo_return_path).toBe("/"); + expect(options.admin_basepath).toBe("/admin"); }); }); - describe('path normalization behavior', () => { - it('should normalize duplicate slashes in settings path', () => { + describe("path normalization behavior", () => { + it("should normalize duplicate slashes in settings path", () => { const options: BkndAdminOptions = { - admin_basepath: '/admin', - logo_return_path: '/' + admin_basepath: "/admin", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); - const result = appReduced.getSettingsPath(['//nested//path//']); - - expect(result).toBe('~/admin/settings/nested/path'); + const result = appReduced.getSettingsPath(["//nested//path//"]); + + expect(result).toBe("~/admin/settings/nested/path"); }); - it('should handle root path normalization', () => { + it("should handle root path normalization", () => { const options: BkndAdminOptions = { - admin_basepath: '/', - logo_return_path: '/' + admin_basepath: "/", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); const result = appReduced.getAbsolutePath(); - + // The normalizeAdminPath function removes trailing slashes except for root "/" // When admin_basepath is "/", the result is "~/" which becomes "~" after normalization - expect(result).toBe('~'); + expect(result).toBe("~"); }); - it('should preserve entity paths ending with slash', () => { + it("should preserve entity paths ending with slash", () => { const options: BkndAdminOptions = { - admin_basepath: '/admin', - logo_return_path: '/' + admin_basepath: "/admin", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); - const result = appReduced.getAbsolutePath('entity/'); - - expect(result).toBe('~/admin/entity/'); + const result = appReduced.getAbsolutePath("entity/"); + + expect(result).toBe("~/admin/entity/"); }); - it('should remove trailing slashes from non-entity paths', () => { + it("should remove trailing slashes from non-entity paths", () => { const options: BkndAdminOptions = { - admin_basepath: '/admin', - logo_return_path: '/' + admin_basepath: "/admin", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); - const result = appReduced.getAbsolutePath('dashboard/'); - - expect(result).toBe('~/admin/dashboard'); + const result = appReduced.getAbsolutePath("dashboard/"); + + expect(result).toBe("~/admin/dashboard"); }); }); - describe('edge cases', () => { - it('should handle undefined admin_basepath', () => { + describe("edge cases", () => { + it("should handle undefined admin_basepath", () => { const options: BkndAdminOptions = { - logo_return_path: '/' + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); const result = appReduced.getSettingsPath(); - + // When admin_basepath is undefined, it defaults to empty string - expect(result).toBe('~/settings'); + expect(result).toBe("~/settings"); }); - it('should handle null path segments', () => { + it("should handle null path segments", () => { const options: BkndAdminOptions = { - admin_basepath: '/admin', - logo_return_path: '/' + admin_basepath: "/admin", + logo_return_path: "/", }; - + appReduced = new AppReduced(mockAppJson, options); - const result = appReduced.getSettingsPath(['', 'valid', '']); - - expect(result).toBe('~/admin/settings/valid'); + const result = appReduced.getSettingsPath(["", "valid", ""]); + + expect(result).toBe("~/admin/settings/valid"); }); }); -}); \ No newline at end of file +}); diff --git a/app/src/ui/client/BkndProvider.tsx b/app/src/ui/client/BkndProvider.tsx index a6e09ff..ad0e154 100644 --- a/app/src/ui/client/BkndProvider.tsx +++ b/app/src/ui/client/BkndProvider.tsx @@ -8,9 +8,12 @@ import { Message } from "ui/components/display/Message"; import { useNavigate } from "ui/lib/routes"; import type { AdminBkndWindowContext } from "modules/server/AdminController"; -export type BkndAdminOptions = Omit & { +export type BkndAdminOptions = Omit< + AdminBkndWindowContext, + "user" | "logout_route" | "admin_basepath" +> & { admin_basepath?: string; -} +}; type BkndContext = { version: number; schema: ModuleSchemas; diff --git a/app/src/ui/client/utils/AppReduced.ts b/app/src/ui/client/utils/AppReduced.ts index 2b703d2..11ead1b 100644 --- a/app/src/ui/client/utils/AppReduced.ts +++ b/app/src/ui/client/utils/AppReduced.ts @@ -36,8 +36,8 @@ export class AppReduced { constructor( protected appJson: AppType, protected _options: BkndAdminOptions = { - admin_basepath: '', - logo_return_path: '/' + admin_basepath: "", + logo_return_path: "/", }, ) { //console.log("received appjson", appJson); @@ -88,20 +88,20 @@ export class AppReduced { get options() { return { - admin_basepath: '', - logo_return_path: '/', + admin_basepath: "", + logo_return_path: "/", ...this._options, }; } getSettingsPath(path: string[] = []): string { - const basePath = this.options.admin_basepath ? `~/${this.options.admin_basepath}` : '~'; - const base = `${basePath}/settings` + const basePath = this.options.admin_basepath ? `~/${this.options.admin_basepath}` : "~"; + const base = `${basePath}/settings`; return normalizeAdminPath([base, ...path].join("/")); } getAbsolutePath(path?: string): string { - const basePath = this.options.admin_basepath ? `~/${this.options.admin_basepath}` : '~'; + const basePath = this.options.admin_basepath ? `~/${this.options.admin_basepath}` : "~"; return normalizeAdminPath(path ? `${basePath}/${path}` : basePath); } diff --git a/app/src/ui/main.css b/app/src/ui/main.css index 280e49d..28cca2d 100644 --- a/app/src/ui/main.css +++ b/app/src/ui/main.css @@ -18,7 +18,7 @@ --color-success-foreground: var(--color-green-800); --color-info: var(--color-blue-100); --color-info-foreground: var(--color-blue-800); - + --color-resize: var(--color-blue-300); @mixin light { diff --git a/app/tsconfig.build.json b/app/tsconfig.build.json index 31ea592..4d813b1 100644 --- a/app/tsconfig.build.json +++ b/app/tsconfig.build.json @@ -1,11 +1,11 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./dist/types", - "rootDir": "./src", - "baseUrl": ".", - "tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo" - }, - "include": ["./src/**/*.ts", "./src/**/*.tsx"], - "exclude": ["./node_modules", "./__test__", "./e2e"] + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/types", + "rootDir": "./src", + "baseUrl": ".", + "tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo" + }, + "include": ["./src/**/*.ts", "./src/**/*.tsx"], + "exclude": ["./node_modules", "./__test__", "./e2e"] } From 64e400c4f1bd18c00bcf794f9bc2d606844a5394 Mon Sep 17 00:00:00 2001 From: dswbx Date: Sun, 3 Aug 2025 13:08:04 +0200 Subject: [PATCH 10/20] ui AppReduced: fixing basepath handling in static and embedded modes --- app/src/ui/client/utils/AppReduced.ts | 38 ++++++++++----------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/app/src/ui/client/utils/AppReduced.ts b/app/src/ui/client/utils/AppReduced.ts index e131317..dd8fb20 100644 --- a/app/src/ui/client/utils/AppReduced.ts +++ b/app/src/ui/client/utils/AppReduced.ts @@ -8,23 +8,6 @@ import type { BkndAdminOptions } from "ui/client/BkndProvider"; export type AppType = ReturnType; -/** - * Normalize admin path by removing duplicate slashes and ensuring proper format - * @param path - The path to normalize - * @returns Normalized path - * @private - */ -function normalizeAdminPath(path: string): string { - // Remove duplicate slashes - const normalized = path.replace(/\/+/g, "/"); - // Don't remove trailing slash if it's the only character or if path ends with entity/ - if (normalized === "/" || normalized.endsWith("/entity/")) { - return normalized; - } - // Remove trailing slash for other paths - return normalized.replace(/\/$/, "") || "/"; -} - /** * Reduced version of the App class for frontend use * @todo: remove this class @@ -37,12 +20,13 @@ export class AppReduced { constructor( protected appJson: AppType, - protected _options: BkndAdminOptions = { + protected _options: BkndAdminOptions & { basepath?: string } = { + basepath: "/", admin_basepath: "", logo_return_path: "/", }, ) { - //console.log("received appjson", appJson); + //console.log("received appjson", _options); this._entities = Object.entries(this.appJson.data.entities ?? {}).map(([name, entity]) => { return constructEntity(name, entity); @@ -90,21 +74,27 @@ export class AppReduced { get options() { return { + basepath: "/", admin_basepath: "", logo_return_path: "/", ...this._options, }; } + withBasePath(path: string | string[], absolute = false): string { + const paths = Array.isArray(path) ? path : [path]; + return [absolute ? "~" : null, this.options.basepath, this.options.admin_basepath, ...paths] + .filter(Boolean) + .join("/") + .replace(/\/+/g, "/"); + } + getSettingsPath(path: string[] = []): string { - const basePath = this.options.admin_basepath ? `~/${this.options.admin_basepath}` : "~"; - const base = `${basePath}/settings`; - return normalizeAdminPath([base, ...path].join("/")); + return this.withBasePath(["settings", ...path], true); } getAbsolutePath(path?: string): string { - const basePath = this.options.admin_basepath ? `~/${this.options.admin_basepath}` : "~"; - return normalizeAdminPath(path ? `${basePath}/${path}` : basePath); + return this.withBasePath(path ?? [], true); } getAuthConfig() { From 2fb07698d55cf4da231fe5d6646b88d17b8fc692 Mon Sep 17 00:00:00 2001 From: dswbx Date: Sun, 3 Aug 2025 13:10:49 +0200 Subject: [PATCH 11/20] fixing AppReduced.spec.ts --- app/__test__/ui/client/utils/AppReduced.spec.ts | 3 ++- app/src/ui/client/utils/AppReduced.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/__test__/ui/client/utils/AppReduced.spec.ts b/app/__test__/ui/client/utils/AppReduced.spec.ts index ca80c2e..459272b 100644 --- a/app/__test__/ui/client/utils/AppReduced.spec.ts +++ b/app/__test__/ui/client/utils/AppReduced.spec.ts @@ -133,6 +133,7 @@ describe("AppReduced", () => { const options = appReduced.options; expect(options).toEqual({ + basepath: "/", logo_return_path: "/custom-home", admin_basepath: "/custom-admin", }); @@ -187,7 +188,7 @@ describe("AppReduced", () => { appReduced = new AppReduced(mockAppJson, options); const result = appReduced.getAbsolutePath("entity/"); - expect(result).toBe("~/admin/entity/"); + expect(result).toBe("~/admin/entity"); }); it("should remove trailing slashes from non-entity paths", () => { diff --git a/app/src/ui/client/utils/AppReduced.ts b/app/src/ui/client/utils/AppReduced.ts index dd8fb20..e34a0c6 100644 --- a/app/src/ui/client/utils/AppReduced.ts +++ b/app/src/ui/client/utils/AppReduced.ts @@ -86,7 +86,8 @@ export class AppReduced { return [absolute ? "~" : null, this.options.basepath, this.options.admin_basepath, ...paths] .filter(Boolean) .join("/") - .replace(/\/+/g, "/"); + .replace(/\/+/g, "/") + .replace(/\/$/, ""); } getSettingsPath(path: string[] = []): string { From 412eab665322c3d3357e54d098ab7c231fce15da Mon Sep 17 00:00:00 2001 From: dswbx Date: Sun, 3 Aug 2025 13:15:01 +0200 Subject: [PATCH 12/20] examples: fixing imports due to 0.16 --- examples/astro/bknd.config.ts | 4 ++-- examples/nextjs/bknd.config.ts | 4 ++-- examples/plasmic/src/server.ts | 23 +++++++++++------------ examples/react-router/bknd.config.ts | 4 ++-- examples/react/src/App.tsx | 5 ++--- examples/waku/bknd.config.ts | 4 ++-- 6 files changed, 21 insertions(+), 23 deletions(-) diff --git a/examples/astro/bknd.config.ts b/examples/astro/bknd.config.ts index 8ddea7e..b7b20bb 100644 --- a/examples/astro/bknd.config.ts +++ b/examples/astro/bknd.config.ts @@ -1,6 +1,6 @@ import type { AstroBkndConfig } from "bknd/adapter/astro"; import { registerLocalMediaAdapter } from "bknd/adapter/node"; -import { boolean, em, entity, text } from "bknd/data"; +import { boolean, em, entity, text } from "bknd"; import { secureRandomString } from "bknd/utils"; // since we're running in node, we can register the local media adapter @@ -16,7 +16,7 @@ const schema = em({ // register your schema to get automatic type completion type Database = (typeof schema)["DB"]; -declare module "bknd/core" { +declare module "bknd" { interface DB extends Database {} } diff --git a/examples/nextjs/bknd.config.ts b/examples/nextjs/bknd.config.ts index 39a12a7..186ef04 100644 --- a/examples/nextjs/bknd.config.ts +++ b/examples/nextjs/bknd.config.ts @@ -1,5 +1,5 @@ import type { NextjsBkndConfig } from "bknd/adapter/nextjs"; -import { boolean, em, entity, text } from "bknd/data"; +import { boolean, em, entity, text } from "bknd"; import { registerLocalMediaAdapter } from "bknd/adapter/node"; import { secureRandomString } from "bknd/utils"; @@ -22,7 +22,7 @@ const schema = em({ // register your schema to get automatic type completion type Database = (typeof schema)["DB"]; -declare module "bknd/core" { +declare module "bknd" { interface DB extends Database {} } diff --git a/examples/plasmic/src/server.ts b/examples/plasmic/src/server.ts index 5ac41cb..e8e931d 100644 --- a/examples/plasmic/src/server.ts +++ b/examples/plasmic/src/server.ts @@ -1,6 +1,5 @@ -import { App } from "bknd"; import { serve } from "bknd/adapter/vite"; -import { boolean, em, entity, text } from "bknd/data"; +import { App, boolean, em, entity, text } from "bknd"; import { secureRandomString } from "bknd/utils"; export default serve({ @@ -8,23 +7,23 @@ export default serve({ data: em({ todos: entity("todos", { title: text(), - done: boolean() - }) + done: boolean(), + }), }).toJSON(), auth: { enabled: true, jwt: { - secret: secureRandomString(64) - } - } + secret: secureRandomString(64), + }, + }, }, options: { seed: async (ctx) => { await ctx.em.mutator("todos" as any).insertMany([ { title: "Learn bknd", done: true }, - { title: "Build something cool", done: false } + { title: "Build something cool", done: false }, ]); - } + }, }, // here we can hook into the app lifecycle events ... beforeBuild: async (app) => { @@ -34,10 +33,10 @@ export default serve({ // ... to create an initial user await app.module.auth.createUser({ email: "ds@bknd.io", - password: "12345678" + password: "12345678", }); }, - "sync" + "sync", ); - } + }, }); diff --git a/examples/react-router/bknd.config.ts b/examples/react-router/bknd.config.ts index 06239b3..8be1756 100644 --- a/examples/react-router/bknd.config.ts +++ b/examples/react-router/bknd.config.ts @@ -1,6 +1,6 @@ import { registerLocalMediaAdapter } from "bknd/adapter/node"; import type { ReactRouterBkndConfig } from "bknd/adapter/react-router"; -import { boolean, em, entity, text } from "bknd/data"; +import { boolean, em, entity, text } from "bknd"; import { secureRandomString } from "bknd/utils"; // since we're running in node, we can register the local media adapter @@ -15,7 +15,7 @@ const schema = em({ // register your schema to get automatic type completion type Database = (typeof schema)["DB"]; -declare module "bknd/core" { +declare module "bknd" { interface DB extends Database {} } diff --git a/examples/react/src/App.tsx b/examples/react/src/App.tsx index f288529..d72cfb5 100644 --- a/examples/react/src/App.tsx +++ b/examples/react/src/App.tsx @@ -1,7 +1,6 @@ import { lazy, Suspense, useEffect, useState } from "react"; -import { App } from "bknd"; import { checksum } from "bknd/utils"; -import { boolean, em, entity, text } from "bknd/data"; +import { App, boolean, em, entity, text } from "bknd"; import { SQLocalConnection } from "@bknd/sqlocal"; import { Route, Router, Switch } from "wouter"; import IndexPage from "~/routes/_index"; @@ -68,7 +67,7 @@ const schema = em({ // register your schema to get automatic type completion type Database = (typeof schema)["DB"]; -declare module "bknd/core" { +declare module "bknd" { interface DB extends Database {} } diff --git a/examples/waku/bknd.config.ts b/examples/waku/bknd.config.ts index 28ecf03..3b384cc 100644 --- a/examples/waku/bknd.config.ts +++ b/examples/waku/bknd.config.ts @@ -1,6 +1,6 @@ import { registerLocalMediaAdapter } from "bknd/adapter/node"; import type { BkndConfig } from "bknd/adapter"; -import { boolean, em, entity, text } from "bknd/data"; +import { boolean, em, entity, text } from "bknd"; import { secureRandomString } from "bknd/utils"; // since we're running in node, we can register the local media adapter @@ -15,7 +15,7 @@ const schema = em({ // register your schema to get automatic type completion type Database = (typeof schema)["DB"]; -declare module "bknd/core" { +declare module "bknd" { interface DB extends Database {} } From 7e01cc61013016e9a8e7583f12c31120b98efee9 Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 30 Dec 2025 06:59:36 -0600 Subject: [PATCH 13/20] Updated bun lockfile --- bun.lock | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bun.lock b/bun.lock index fa63422..3ccb778 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "bknd", @@ -15,10 +16,11 @@ }, "app": { "name": "bknd", - "version": "0.18.1", + "version": "0.19.0", "bin": "./dist/cli/index.js", "dependencies": { "@cfworker/json-schema": "^4.1.1", + "@clack/prompts": "^0.11.0", "@codemirror/lang-html": "^6.4.9", "@codemirror/lang-json": "^6.0.1", "@hello-pangea/dnd": "^18.0.1", @@ -1243,7 +1245,7 @@ "@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="], - "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], + "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="], "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], @@ -4095,7 +4097,7 @@ "@testing-library/jest-dom/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], - "@types/bun/bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], + "@types/bun/bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], "@typescript-eslint/experimental-utils/eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="], From 19e51fae63f536304c6807341cc0c34472b35bd3 Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 30 Dec 2025 07:09:00 -0600 Subject: [PATCH 14/20] Refactor AppReduced to use `basepath` config option The `admin_basepath` configuration option has been renamed to `basepath` for clarity and consistency. This change affects how the admin base path is accessed and utilized within the `AppReduced` utility. Tests have been updated to reflect this renaming. --- .../ui/client/utils/AppReduced.spec.ts | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/app/__test__/ui/client/utils/AppReduced.spec.ts b/app/__test__/ui/client/utils/AppReduced.spec.ts index 459272b..7afd838 100644 --- a/app/__test__/ui/client/utils/AppReduced.spec.ts +++ b/app/__test__/ui/client/utils/AppReduced.spec.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest"; import { AppReduced, type AppType } from "ui/client/utils/AppReduced"; -import type { BkndAdminOptions } from "ui/client/BkndProvider"; +import type { BkndAdminProps } from "ui/Admin"; // Import the normalizeAdminPath function for testing // Note: This assumes the function is exported or we need to test it indirectly through public methods @@ -23,9 +23,9 @@ describe("AppReduced", () => { }); describe("getSettingsPath", () => { - it("should return settings path with admin_basepath", () => { - const options: BkndAdminOptions = { - admin_basepath: "/admin", + it("should return settings path with basepath", () => { + const options: BkndAdminProps["config"] = { + basepath: "/admin", logo_return_path: "/", }; @@ -35,9 +35,9 @@ describe("AppReduced", () => { expect(result).toBe("~/admin/settings"); }); - it("should return settings path with empty admin_basepath", () => { - const options: BkndAdminOptions = { - admin_basepath: "", + it("should return settings path with empty basepath", () => { + const options: BkndAdminProps["config"] = { + basepath: "", logo_return_path: "/", }; @@ -48,8 +48,8 @@ describe("AppReduced", () => { }); it("should append additional path segments", () => { - const options: BkndAdminOptions = { - admin_basepath: "/admin", + const options: BkndAdminProps["config"] = { + basepath: "/admin", logo_return_path: "/", }; @@ -60,8 +60,8 @@ describe("AppReduced", () => { }); it("should normalize multiple slashes", () => { - const options: BkndAdminOptions = { - admin_basepath: "//admin//", + const options: BkndAdminProps["config"] = { + basepath: "//admin//", logo_return_path: "/", }; @@ -71,9 +71,9 @@ describe("AppReduced", () => { expect(result).toBe("~/admin/settings/user"); }); - it("should handle admin_basepath without leading slash", () => { - const options: BkndAdminOptions = { - admin_basepath: "admin", + it("should handle basepath without leading slash", () => { + const options: BkndAdminProps["config"] = { + basepath: "admin", logo_return_path: "/", }; @@ -85,9 +85,9 @@ describe("AppReduced", () => { }); describe("getAbsolutePath", () => { - it("should return absolute path with admin_basepath", () => { - const options: BkndAdminOptions = { - admin_basepath: "/admin", + it("should return absolute path with basepath", () => { + const options: BkndAdminProps["config"] = { + basepath: "/admin", logo_return_path: "/", }; @@ -98,8 +98,8 @@ describe("AppReduced", () => { }); it("should return base path when no path provided", () => { - const options: BkndAdminOptions = { - admin_basepath: "/admin", + const options: BkndAdminProps["config"] = { + basepath: "/admin", logo_return_path: "/", }; @@ -110,8 +110,8 @@ describe("AppReduced", () => { }); it("should normalize paths correctly", () => { - const options: BkndAdminOptions = { - admin_basepath: "//admin//", + const options: BkndAdminProps["config"] = { + basepath: "//admin//", logo_return_path: "/", }; @@ -124,8 +124,8 @@ describe("AppReduced", () => { describe("options getter", () => { it("should return merged options with defaults", () => { - const customOptions: BkndAdminOptions = { - admin_basepath: "/custom-admin", + const customOptions: BkndAdminProps["config"] = { + basepath: "/custom-admin", logo_return_path: "/custom-home", }; @@ -135,27 +135,27 @@ describe("AppReduced", () => { expect(options).toEqual({ basepath: "/", logo_return_path: "/custom-home", - admin_basepath: "/custom-admin", + basepath: "/custom-admin", }); }); it("should use default logo_return_path when not provided", () => { - const customOptions: BkndAdminOptions = { - admin_basepath: "/admin", + const customOptions: BkndAdminProps["config"] = { + basepath: "/admin", }; appReduced = new AppReduced(mockAppJson, customOptions); const options = appReduced.options; expect(options.logo_return_path).toBe("/"); - expect(options.admin_basepath).toBe("/admin"); + expect(options.basepath).toBe("/admin"); }); }); describe("path normalization behavior", () => { it("should normalize duplicate slashes in settings path", () => { - const options: BkndAdminOptions = { - admin_basepath: "/admin", + const options: BkndAdminProps["config"] = { + basepath: "/admin", logo_return_path: "/", }; @@ -166,8 +166,8 @@ describe("AppReduced", () => { }); it("should handle root path normalization", () => { - const options: BkndAdminOptions = { - admin_basepath: "/", + const options: BkndAdminProps["config"] = { + basepath: "/", logo_return_path: "/", }; @@ -175,13 +175,13 @@ describe("AppReduced", () => { const result = appReduced.getAbsolutePath(); // The normalizeAdminPath function removes trailing slashes except for root "/" - // When admin_basepath is "/", the result is "~/" which becomes "~" after normalization + // When basepath is "/", the result is "~/" which becomes "~" after normalization expect(result).toBe("~"); }); it("should preserve entity paths ending with slash", () => { - const options: BkndAdminOptions = { - admin_basepath: "/admin", + const options: BkndAdminProps["config"] = { + basepath: "/admin", logo_return_path: "/", }; @@ -192,8 +192,8 @@ describe("AppReduced", () => { }); it("should remove trailing slashes from non-entity paths", () => { - const options: BkndAdminOptions = { - admin_basepath: "/admin", + const options: BkndAdminProps["config"] = { + basepath: "/admin", logo_return_path: "/", }; @@ -205,21 +205,21 @@ describe("AppReduced", () => { }); describe("edge cases", () => { - it("should handle undefined admin_basepath", () => { - const options: BkndAdminOptions = { + it("should handle undefined basepath", () => { + const options: BkndAdminProps["config"] = { logo_return_path: "/", }; appReduced = new AppReduced(mockAppJson, options); const result = appReduced.getSettingsPath(); - // When admin_basepath is undefined, it defaults to empty string + // When basepath is undefined, it defaults to empty string expect(result).toBe("~/settings"); }); it("should handle null path segments", () => { - const options: BkndAdminOptions = { - admin_basepath: "/admin", + const options: BkndAdminProps["config"] = { + basepath: "/admin", logo_return_path: "/", }; From 6faf0d421fa7bb9085c2de10aeba9d5634b40224 Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 30 Dec 2025 07:33:55 -0600 Subject: [PATCH 15/20] Fix test for Blob to File conversion The Response.blob() method in JavaScript returns a Blob, not a File. This means that file-specific metadata like `name` and `lastModified` is lost. The tests were updated to reflect this by manually constructing a File object from the Blob, mimicking how client code would handle this conversion. The MIME type is now correctly preserved from the response headers when creating the File. --- app/__test__/api/MediaApi.spec.ts | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/app/__test__/api/MediaApi.spec.ts b/app/__test__/api/MediaApi.spec.ts index 95cac8e..95ffee0 100644 --- a/app/__test__/api/MediaApi.spec.ts +++ b/app/__test__/api/MediaApi.spec.ts @@ -99,10 +99,13 @@ describe("MediaApi", () => { expect(isReadableStream(res.res.body)).toBe(true); const blob = await res.res.blob(); - expect(isFile(blob)).toBe(true); - expect(blob.size).toBeGreaterThan(0); - expect(blob.type).toBe("image/png"); - expect(blob.name).toContain(name); + // Response.blob() always returns Blob, not File - File metadata (name, lastModified) is lost + // Client code must manually construct File from Blob (see MediaApi.download() for reference) + const file = new File([blob], name, { type: blob.type }); + expect(isFile(file)).toBe(true); + expect(file.size).toBeGreaterThan(0); + expect(file.type).toBe("image/png"); + expect(file.name).toContain(name); }); it("getFileStream", async () => { @@ -110,14 +113,19 @@ describe("MediaApi", () => { const api = new MediaApi({}, mockedBackend.request); const name = "image.png"; - const res = await api.getFileStream(name); - expect(isReadableStream(res)).toBe(true); + const { res: originalRes } = await api.getFile(name); + const stream = await api.getFileStream(name); + expect(isReadableStream(stream)).toBe(true); - const blob = await new Response(res).blob(); - expect(isFile(blob)).toBe(true); - expect(blob.size).toBeGreaterThan(0); - expect(blob.type).toBe("image/png"); - expect(blob.name).toContain(name); + const blob = await new Response(stream).blob(); + // Response.blob() always returns Blob, not File - File metadata (name, lastModified) is lost + // Client code must manually construct File from Blob (see MediaApi.download() for reference) + // Use originalRes.headers.get("Content-Type") to preserve MIME type from response + const file = new File([blob], name, { type: originalRes.headers.get("Content-Type") || blob.type }); + expect(isFile(file)).toBe(true); + expect(file.size).toBeGreaterThan(0); + expect(file.type).toBe("image/png"); + expect(file.name).toContain(name); }); it("should upload file in various ways", async () => { From d00e5fe68cf037461dfb61a85a3a15d720060a1d Mon Sep 17 00:00:00 2001 From: cameronapak Date: Tue, 30 Dec 2025 07:34:00 -0600 Subject: [PATCH 16/20] Remove redundant basepath option --- app/__test__/ui/client/utils/AppReduced.spec.ts | 1 - app/src/ui/client/utils/AppReduced.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/app/__test__/ui/client/utils/AppReduced.spec.ts b/app/__test__/ui/client/utils/AppReduced.spec.ts index 7afd838..1aafba9 100644 --- a/app/__test__/ui/client/utils/AppReduced.spec.ts +++ b/app/__test__/ui/client/utils/AppReduced.spec.ts @@ -133,7 +133,6 @@ describe("AppReduced", () => { const options = appReduced.options; expect(options).toEqual({ - basepath: "/", logo_return_path: "/custom-home", basepath: "/custom-admin", }); diff --git a/app/src/ui/client/utils/AppReduced.ts b/app/src/ui/client/utils/AppReduced.ts index 6b3cc70..38816c9 100644 --- a/app/src/ui/client/utils/AppReduced.ts +++ b/app/src/ui/client/utils/AppReduced.ts @@ -71,7 +71,6 @@ export class AppReduced { get options() { return { basepath: "/", - admin_basepath: "", logo_return_path: "/", ...this._options, }; From eb22289460aea018fe05a9d277ceb356dde77e1b Mon Sep 17 00:00:00 2001 From: Cameron Pak Date: Sat, 28 Feb 2026 11:00:37 -0600 Subject: [PATCH 17/20] Update @types/bun to 1.3.9 This commit updates the `@types/bun` package to version 1.3.9. --- bun.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bun.lock b/bun.lock index 3ccb778..c4c26e2 100644 --- a/bun.lock +++ b/bun.lock @@ -1245,7 +1245,7 @@ "@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="], - "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="], + "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="], "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], @@ -4097,7 +4097,7 @@ "@testing-library/jest-dom/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], - "@types/bun/bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], + "@types/bun/bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="], "@typescript-eslint/experimental-utils/eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="], From 9008f9c6a303533f39e3c0390ec1df99ea998472 Mon Sep 17 00:00:00 2001 From: Cameron Pak Date: Sat, 28 Feb 2026 11:11:44 -0600 Subject: [PATCH 18/20] fix: normalize admin basepath to prevent double slashes in URLs Strip trailing slashes from adminBasepath in AdminController window context and wouter Router base to prevent paths like /admin//data when users configure adminBasepath with a trailing slash. Amp-Thread-ID: https://ampcode.com/threads/T-019ca537-4cc4-7174-bf9a-5325d782f097 Co-authored-by: Amp --- .../ui/client/utils/AppReduced.spec.ts | 57 +++++++++++++++++++ app/src/modules/server/AdminController.tsx | 2 +- app/src/ui/routes/index.tsx | 2 +- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/app/__test__/ui/client/utils/AppReduced.spec.ts b/app/__test__/ui/client/utils/AppReduced.spec.ts index 1aafba9..58a7b3f 100644 --- a/app/__test__/ui/client/utils/AppReduced.spec.ts +++ b/app/__test__/ui/client/utils/AppReduced.spec.ts @@ -203,6 +203,63 @@ describe("AppReduced", () => { }); }); + describe("withBasePath - double slash fix (admin_basepath with trailing slash)", () => { + it("should not produce double slashes when admin_basepath has trailing slash", () => { + const options: BkndAdminProps["config"] = { + basepath: "/", + admin_basepath: "/admin/", + logo_return_path: "/", + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.withBasePath("/data"); + + expect(result).toBe("/admin/data"); + expect(result).not.toContain("//"); + }); + + it("should work correctly when admin_basepath has no trailing slash", () => { + const options: BkndAdminProps["config"] = { + basepath: "/", + admin_basepath: "/admin", + logo_return_path: "/", + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.withBasePath("/data"); + + expect(result).toBe("/admin/data"); + }); + + it("should handle absolute paths with admin_basepath trailing slash", () => { + const options: BkndAdminProps["config"] = { + basepath: "/", + admin_basepath: "/admin/", + logo_return_path: "/", + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getAbsolutePath("data"); + + expect(result).toBe("~/admin/data"); + expect(result).not.toContain("//"); + }); + + it("should handle settings path with admin_basepath trailing slash", () => { + const options: BkndAdminProps["config"] = { + basepath: "/", + admin_basepath: "/admin/", + logo_return_path: "/", + }; + + appReduced = new AppReduced(mockAppJson, options); + const result = appReduced.getSettingsPath(["general"]); + + expect(result).toBe("~/admin/settings/general"); + expect(result).not.toContain("//"); + }); + }); + describe("edge cases", () => { it("should handle undefined basepath", () => { const options: BkndAdminProps["config"] = { diff --git a/app/src/modules/server/AdminController.tsx b/app/src/modules/server/AdminController.tsx index a579ebb..d7592c5 100644 --- a/app/src/modules/server/AdminController.tsx +++ b/app/src/modules/server/AdminController.tsx @@ -123,7 +123,7 @@ export class AdminController extends Controller { const obj: AdminBkndWindowContext = { user: c.get("auth")?.user, logout_route: authRoutes.logout, - admin_basepath: this.options.adminBasepath, + admin_basepath: this.options.adminBasepath.replace(/\/+$/, ""), theme: this.options.theme, logo_return_path: this.options.logoReturnPath, }; diff --git a/app/src/ui/routes/index.tsx b/app/src/ui/routes/index.tsx index 6b3a3e4..de83a07 100644 --- a/app/src/ui/routes/index.tsx +++ b/app/src/ui/routes/index.tsx @@ -33,7 +33,7 @@ export function Routes({ }) { const { theme } = useTheme(); const ctx = useBkndWindowContext(); - const actualBasePath = basePath || ctx.admin_basepath; + const actualBasePath = (basePath || ctx.admin_basepath).replace(/\/+$/, ""); return (
From fb429e57e251da83265302a6d7165f9c1c965897 Mon Sep 17 00:00:00 2001 From: Cameron Pak Date: Sat, 28 Feb 2026 11:21:39 -0600 Subject: [PATCH 19/20] fix: resolve undefined variable in MediaApi test and remove dead comment Amp-Thread-ID: https://ampcode.com/threads/T-019ca53d-a225-706c-923e-28123c84d83e Co-authored-by: Amp --- app/__test__/api/MediaApi.spec.ts | 2 +- app/src/ui/client/utils/AppReduced.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/__test__/api/MediaApi.spec.ts b/app/__test__/api/MediaApi.spec.ts index 779c0e7..45d540d 100644 --- a/app/__test__/api/MediaApi.spec.ts +++ b/app/__test__/api/MediaApi.spec.ts @@ -110,7 +110,7 @@ describe("MediaApi", () => { const api = new MediaApi({}, mockedBackend.request); const name = "image.png"; - const { res: originalRes } = await api.getFile(name); + const { res } = await api.getFile(name); const stream = await api.getFileStream(name); expect(isReadableStream(stream)).toBe(true); diff --git a/app/src/ui/client/utils/AppReduced.ts b/app/src/ui/client/utils/AppReduced.ts index 38816c9..353faa4 100644 --- a/app/src/ui/client/utils/AppReduced.ts +++ b/app/src/ui/client/utils/AppReduced.ts @@ -22,7 +22,6 @@ export class AppReduced { protected appJson: AppType, protected _options: BkndAdminProps["config"] = {}, ) { - //console.log("received appjson", _options); this._entities = Object.entries(this.appJson.data.entities ?? {}).map(([name, entity]) => { return constructEntity(name, entity); From 367d5878df33e28eae6b137551b636d81ce13f85 Mon Sep 17 00:00:00 2001 From: Cameron Pak Date: Sat, 28 Feb 2026 11:26:27 -0600 Subject: [PATCH 20/20] Fixed test --- app/__test__/api/MediaApi.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/__test__/api/MediaApi.spec.ts b/app/__test__/api/MediaApi.spec.ts index 45d540d..b17962c 100644 --- a/app/__test__/api/MediaApi.spec.ts +++ b/app/__test__/api/MediaApi.spec.ts @@ -110,11 +110,11 @@ describe("MediaApi", () => { const api = new MediaApi({}, mockedBackend.request); const name = "image.png"; - const { res } = await api.getFile(name); + const res = await api.getFile(name); const stream = await api.getFileStream(name); expect(isReadableStream(stream)).toBe(true); - const blob = (await new Response(res).blob()) as File; + const blob = (await res.res.blob()) as File; expect(isFile(blob)).toBe(true); expect(blob.size).toBeGreaterThan(0); expect(blob.type).toBe("image/png");