diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..bf2e764 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +shamefully-hoist=true diff --git a/build.js b/build.js new file mode 100644 index 0000000..07c01ce --- /dev/null +++ b/build.js @@ -0,0 +1,109 @@ +const sleep = x => new Promise(res => setTimeout(() => res(), x * 1000)) + +;(async () => { + const { spawn } = require('node:child_process') + const ora = (await import('ora')).default + + class Builder { + constructor() { + this.spinner = ora('Starting build').start() + this.contextMaxLines = 3 + this.contextLines = Array(this.contextMaxLines).fill('') + this.allLines = [] + this.steps = [] + this.renderLines() + } + + pushLine(line) { + if ( this.contextLines.length >= this.contextMaxLines ) this.contextLines = this.contextLines.slice(1) + this.contextLines.push(String(line).trimEnd()) + this.allLines.push(String(line).trimEnd()) + this.renderLines() + } + + renderLines() { + this.spinner.suffixText = `\n--\n${this.contextLines.join('\n')}\n--` + } + + addStep(name, callback) { + this.steps.push({ name, callback }) + } + + async build() { + for ( let i = 0; i < this.steps.length; i += 1 ) { + const step = this.steps[i] + this.spinner.text = `(${i+1}/${this.steps.length}) ${step.name}` + await step.callback(this) + } + + this.spinner.suffixText = '' + this.spinner.succeed('Built successfully') + } + + async executeOrExit(prog, ...args) { + try { + return await this.execute(prog, ...args) + } catch (e) { + this.spinner.suffixText = '' + this.spinner.fail(`${this.spinner.text} - ${e.message}`) + console.log(this.allLines.join('\n')) + process.exit(1) + } + } + + execute(prog, ...args) { + this.pushLine(`> ${prog} ${args.join(' ')}`) + + const cmd = spawn(prog, args) + let output = '' + + cmd.stdout.on('data', data => { + output += data + String(data).split('\n').map(x => this.pushLine(x)) + }) + cmd.stderr.on('data', data => { + output += data + String(data).split('\n').map(x => this.pushLine(x)) + }) + + return new Promise((res, rej) => { + cmd.on('close', code => { + if ( code ) { + return rej(new Error('Process exited with code: ' + code)) + } else { + res(output.trim()) + } + }) + }) + } + } + + const b= new Builder + + b.addStep( + 'Remove old build files', + b => b.executeOrExit('./node_modules/.bin/rimraf', 'lib'), + ) + + b.addStep( + 'Build back-end TypeScript code', + b => b.executeOrExit('./node_modules/.bin/tsc', '-p', 'tsconfig.node.json'), + ) + + b.addStep( + 'Copy resources to output bundle', + b => b.executeOrExit('./node_modules/.bin/fse', 'copy', '--all', '--dereference', '--preserveTimestamps', '--keepExisting=false', '--quiet', '--errorOnExist=false', 'src/app/resources', 'lib/app/resources'), + ) + + b.addStep( + 'Build front-end TypeScript code', + b => b.executeOrExit('./node_modules/.bin/tsc', '-p', 'tsconfig.client.json'), + ) + + b.addStep( + 'Create front-end output bundle', + b => b.executeOrExit('./node_modules/.bin/webpack', '--config', 'webpack.config.js'), + ) + + await b.build() +})(); diff --git a/deploy/deployment.yaml b/deploy/deployment.yaml index 4341dfa..5805740 100644 --- a/deploy/deployment.yaml +++ b/deploy/deployment.yaml @@ -30,6 +30,17 @@ spec: containers: - name: garrettmills-www image: registry.millslan.net/garrettmills/www + livenessProbe: + httpGet: + path: / + port: 8000 + initialDelaySeconds: 60 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 8000 + initialDelaySeconds: 30 env: - name: EXTOLLO_LOGGING_LEVEL value: '4' diff --git a/package.json b/package.json index 4c69cb2..39edb42 100644 --- a/package.json +++ b/package.json @@ -9,16 +9,20 @@ }, "dependencies": { "@atao60/fse-cli": "^0.1.7", + "@codemirror/lang-html": "^6.4.9", "@extollo/lib": "^0.14.14", + "@lit/task": "^1.0.1", "@types/marked": "^4.0.8", "@types/node": "^18.19.39", "@types/xml2js": "^0.4.11", "any-date-parser": "^1.5.3", + "codemirror": "^6.0.1", "copyfiles": "^2.4.1", "feed": "^4.2.2", "gotify": "^1.1.0", "gray-matter": "^4.0.3", "lib": "link:@extollo/lib:../extollo/lib", + "lit": "^3.1.4", "marked": "^4.2.12", "marked-footnote": "^1.2.2", "ts-expose-internals": "^4.5.4", @@ -31,7 +35,7 @@ }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "build": "pnpm run clean && tsc -p tsconfig.node.json && fse copy --all --dereference --preserveTimestamps --keepExisting=false --quiet --errorOnExist=false src/app/resources lib/app/resources && tsc -p tsconfig.client.json", + "build": "node build.js", "clean": "rimraf lib", "watch": "nodemon --ext js,pug,ts --watch src --exec 'ts-node src/index.ts'", "app": "ts-node src/index.ts", @@ -63,6 +67,11 @@ }, "devDependencies": { "@extollo/cc": "^0.6.0", - "rimraf": "^3.0.2" + "css-loader": "^7.1.2", + "ora": "^8.0.1", + "rimraf": "^3.0.2", + "style-loader": "^4.0.0", + "webpack": "^5.93.0", + "webpack-cli": "^5.1.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1610730..a411dfd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,15 @@ dependencies: '@atao60/fse-cli': specifier: ^0.1.7 version: 0.1.7 + '@codemirror/lang-html': + specifier: ^6.4.9 + version: 6.4.9 '@extollo/lib': specifier: ^0.14.14 version: 0.14.14 + '@lit/task': + specifier: ^1.0.1 + version: 1.0.1 '@types/marked': specifier: ^4.0.8 version: 4.0.8 @@ -23,6 +29,9 @@ dependencies: any-date-parser: specifier: ^1.5.3 version: 1.5.3 + codemirror: + specifier: ^6.0.1 + version: 6.0.1(@lezer/common@1.2.1) copyfiles: specifier: ^2.4.1 version: 2.4.1 @@ -38,6 +47,9 @@ dependencies: lib: specifier: link:@extollo/lib:../extollo/lib version: link:@extollo/lib:../extollo/lib + lit: + specifier: ^3.1.4 + version: 3.1.4 marked: specifier: ^4.2.12 version: 4.2.12 @@ -70,9 +82,24 @@ devDependencies: '@extollo/cc': specifier: ^0.6.0 version: 0.6.0(@types/node@18.19.39) + css-loader: + specifier: ^7.1.2 + version: 7.1.2(webpack@5.93.0) + ora: + specifier: ^8.0.1 + version: 8.0.1 rimraf: specifier: ^3.0.2 version: 3.0.2 + style-loader: + specifier: ^4.0.0 + version: 4.0.0(webpack@5.93.0) + webpack: + specifier: ^5.93.0 + version: 5.93.0(webpack-cli@5.1.4) + webpack-cli: + specifier: ^5.1.4 + version: 5.1.4(webpack@5.93.0) packages: @@ -94,8 +121,8 @@ packages: tslib: 2.4.1 dev: false - /@babel/helper-string-parser@7.24.7: - resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} + /@babel/helper-string-parser@7.24.8: + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} engines: {node: '>=6.9.0'} dev: false @@ -104,12 +131,12 @@ packages: engines: {node: '>=6.9.0'} dev: false - /@babel/parser@7.24.7: - resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} + /@babel/parser@7.24.8: + resolution: {integrity: sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.24.9 dev: false /@babel/runtime@7.20.1: @@ -119,21 +146,126 @@ packages: regenerator-runtime: 0.13.10 dev: false - /@babel/types@7.24.7: - resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + /@babel/types@7.24.9: + resolution: {integrity: sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-string-parser': 7.24.7 + '@babel/helper-string-parser': 7.24.8 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 dev: false + /@codemirror/autocomplete@6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.28.6)(@lezer/common@1.2.1): + resolution: {integrity: sha512-fdfj6e6ZxZf8yrkMHUSJJir7OJkHkZKaOZGzLWIYp2PZ3jd+d+UjG8zVPqJF6d3bKxkhvXTPan/UZ1t7Bqm0gA==} + peerDependencies: + '@codemirror/language': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + '@lezer/common': ^1.0.0 + dependencies: + '@codemirror/language': 6.10.2 + '@codemirror/state': 6.4.1 + '@codemirror/view': 6.28.6 + '@lezer/common': 1.2.1 + dev: false + + /@codemirror/commands@6.6.0: + resolution: {integrity: sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==} + dependencies: + '@codemirror/language': 6.10.2 + '@codemirror/state': 6.4.1 + '@codemirror/view': 6.28.6 + '@lezer/common': 1.2.1 + dev: false + + /@codemirror/lang-css@6.2.1(@codemirror/view@6.28.6): + resolution: {integrity: sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==} + dependencies: + '@codemirror/autocomplete': 6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.28.6)(@lezer/common@1.2.1) + '@codemirror/language': 6.10.2 + '@codemirror/state': 6.4.1 + '@lezer/common': 1.2.1 + '@lezer/css': 1.1.8 + transitivePeerDependencies: + - '@codemirror/view' + dev: false + + /@codemirror/lang-html@6.4.9: + resolution: {integrity: sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==} + dependencies: + '@codemirror/autocomplete': 6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.28.6)(@lezer/common@1.2.1) + '@codemirror/lang-css': 6.2.1(@codemirror/view@6.28.6) + '@codemirror/lang-javascript': 6.2.2 + '@codemirror/language': 6.10.2 + '@codemirror/state': 6.4.1 + '@codemirror/view': 6.28.6 + '@lezer/common': 1.2.1 + '@lezer/css': 1.1.8 + '@lezer/html': 1.3.10 + dev: false + + /@codemirror/lang-javascript@6.2.2: + resolution: {integrity: sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==} + dependencies: + '@codemirror/autocomplete': 6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.28.6)(@lezer/common@1.2.1) + '@codemirror/language': 6.10.2 + '@codemirror/lint': 6.8.1 + '@codemirror/state': 6.4.1 + '@codemirror/view': 6.28.6 + '@lezer/common': 1.2.1 + '@lezer/javascript': 1.4.17 + dev: false + + /@codemirror/language@6.10.2: + resolution: {integrity: sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==} + dependencies: + '@codemirror/state': 6.4.1 + '@codemirror/view': 6.28.6 + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.4.1 + style-mod: 4.1.2 + dev: false + + /@codemirror/lint@6.8.1: + resolution: {integrity: sha512-IZ0Y7S4/bpaunwggW2jYqwLuHj0QtESf5xcROewY6+lDNwZ/NzvR4t+vpYgg9m7V8UXLPYqG+lu3DF470E5Oxg==} + dependencies: + '@codemirror/state': 6.4.1 + '@codemirror/view': 6.28.6 + crelt: 1.0.6 + dev: false + + /@codemirror/search@6.5.6: + resolution: {integrity: sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==} + dependencies: + '@codemirror/state': 6.4.1 + '@codemirror/view': 6.28.6 + crelt: 1.0.6 + dev: false + + /@codemirror/state@6.4.1: + resolution: {integrity: sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==} + dev: false + + /@codemirror/view@6.28.6: + resolution: {integrity: sha512-bhwB1AZ6zU4M3dNKm8Aa2BXwj5mWDqE9IWpqxYKJoLCnx+AcwcMuLO01tLWgc1mx4vT1IVYVqx86YoqUsATrqQ==} + dependencies: + '@codemirror/state': 6.4.1 + style-mod: 4.1.2 + w3c-keyname: 2.2.8 + dev: false + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 + /@discoveryjs/json-ext@0.5.7: + resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} + engines: {node: '>=10.0.0'} + dev: true + /@extollo/cc@0.6.0(@types/node@18.19.39): resolution: {integrity: sha512-AOCx6TXdGBamz+mPSl32oH7i1F8lKoAjVAAJEoP4IsaYmK1T8IdYRgstfTpslTeqwoFTz2UiCqWRrzikYpp22g==} hasBin: true @@ -184,7 +316,7 @@ packages: '@types/rimraf': 3.0.2 '@types/ssh2': 0.5.52 '@types/uuid': 8.3.4 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 bcrypt: 5.1.1 busboy: 0.3.1 cli-table: 0.3.11 @@ -207,7 +339,7 @@ packages: ts-node: 10.9.2(@types/node@14.18.63)(typescript@4.9.5) typescript: 4.9.5 uuid: 8.3.2 - ws: 8.17.1 + ws: 8.18.0 zod: 3.23.8 transitivePeerDependencies: - '@swc/core' @@ -244,19 +376,103 @@ packages: dev: false optional: true + /@jridgewell/gen-mapping@0.3.5: + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/trace-mapping': 0.3.25 + dev: true + /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} + /@jridgewell/set-array@1.2.1: + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/source-map@0.3.6: + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + dev: true + /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + /@jridgewell/trace-mapping@0.3.25: + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 + /@lezer/common@1.2.1: + resolution: {integrity: sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==} + dev: false + + /@lezer/css@1.1.8: + resolution: {integrity: sha512-7JhxupKuMBaWQKjQoLtzhGj83DdnZY9MckEOG5+/iLKNK2ZJqKc6hf6uc0HjwCX7Qlok44jBNqZhHKDhEhZYLA==} + dependencies: + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.4.1 + dev: false + + /@lezer/highlight@1.2.0: + resolution: {integrity: sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==} + dependencies: + '@lezer/common': 1.2.1 + dev: false + + /@lezer/html@1.3.10: + resolution: {integrity: sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==} + dependencies: + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.4.1 + dev: false + + /@lezer/javascript@1.4.17: + resolution: {integrity: sha512-bYW4ctpyGK+JMumDApeUzuIezX01H76R1foD6LcRX224FWfyYit/HYxiPGDjXXe/wQWASjCvVGoukTH68+0HIA==} + dependencies: + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.4.1 + dev: false + + /@lezer/lr@1.4.1: + resolution: {integrity: sha512-CHsKq8DMKBf9b3yXPDIU4DbH+ZJd/sJdYOW2llbW/HudP5u0VS6Bfq1hLYfgU7uAYGFIyGGQIsSOXGPEErZiJw==} + dependencies: + '@lezer/common': 1.2.1 + dev: false + + /@lit-labs/ssr-dom-shim@1.2.0: + resolution: {integrity: sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==} + dev: false + + /@lit/reactive-element@2.0.4: + resolution: {integrity: sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==} + dependencies: + '@lit-labs/ssr-dom-shim': 1.2.0 + dev: false + + /@lit/task@1.0.1: + resolution: {integrity: sha512-fVLDtmwCau8NywnFIXaJxsCZjzaIxnVq+cFRKYC1Y4tA4/0rMTvF6DLZZ2JE51BwzOluaKtgJX8x1QDsQtAaIw==} + dependencies: + '@lit/reactive-element': 2.0.4 + dev: false + /@mapbox/node-pre-gyp@1.0.11: resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} hasBin: true @@ -268,7 +484,7 @@ packages: nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.3.8 + semver: 7.6.3 tar: 6.2.1 transitivePeerDependencies: - encoding @@ -297,7 +513,7 @@ packages: resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} dependencies: '@gar/promisify': 1.1.3 - semver: 7.3.8 + semver: 7.6.3 dev: false optional: true @@ -322,7 +538,7 @@ packages: '@oclif/help': 1.0.4 '@oclif/parser': 3.8.9 debug: 4.3.4 - semver: 7.3.8 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -337,7 +553,7 @@ packages: '@oclif/help': 1.0.4 '@oclif/parser': 3.8.9 debug: 4.3.4 - semver: 7.3.8 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -500,6 +716,24 @@ packages: resolution: {integrity: sha512-GsALrTL69mlwbAw/MHF1IPTadSLZQnsxe7a80G8l4inN/iEXCOcVeT/S7aRc6hbhqzL9qZ314kHPDQnQ3ev+HA==} dev: false + /@types/eslint-scope@3.7.7: + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + dependencies: + '@types/eslint': 8.56.10 + '@types/estree': 1.0.5 + dev: true + + /@types/eslint@8.56.10: + resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} + dependencies: + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true + /@types/fs-extra@9.0.13: resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} dependencies: @@ -522,6 +756,10 @@ packages: '@types/node': 18.19.39 dev: false + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + /@types/jsonwebtoken@8.5.9: resolution: {integrity: sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==} dependencies: @@ -604,11 +842,15 @@ packages: '@types/ssh2-streams': 0.1.12 dev: false + /@types/trusted-types@2.0.7: + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + dev: false + /@types/uuid@8.3.4: resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} - /@types/ws@8.5.10: - resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} + /@types/ws@8.5.11: + resolution: {integrity: sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==} dependencies: '@types/node': 18.19.39 dev: false @@ -626,10 +868,169 @@ packages: transitivePeerDependencies: - supports-color + /@webassemblyjs/ast@1.12.1: + resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} + dependencies: + '@webassemblyjs/helper-numbers': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + dev: true + + /@webassemblyjs/floating-point-hex-parser@1.11.6: + resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} + dev: true + + /@webassemblyjs/helper-api-error@1.11.6: + resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} + dev: true + + /@webassemblyjs/helper-buffer@1.12.1: + resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==} + dev: true + + /@webassemblyjs/helper-numbers@1.11.6: + resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 + '@xtuc/long': 4.2.2 + dev: true + + /@webassemblyjs/helper-wasm-bytecode@1.11.6: + resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} + dev: true + + /@webassemblyjs/helper-wasm-section@1.12.1: + resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==} + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/wasm-gen': 1.12.1 + dev: true + + /@webassemblyjs/ieee754@1.11.6: + resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} + dependencies: + '@xtuc/ieee754': 1.2.0 + dev: true + + /@webassemblyjs/leb128@1.11.6: + resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} + dependencies: + '@xtuc/long': 4.2.2 + dev: true + + /@webassemblyjs/utf8@1.11.6: + resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} + dev: true + + /@webassemblyjs/wasm-edit@1.12.1: + resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==} + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-wasm-section': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-opt': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + '@webassemblyjs/wast-printer': 1.12.1 + dev: true + + /@webassemblyjs/wasm-gen@1.12.1: + resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==} + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + dev: true + + /@webassemblyjs/wasm-opt@1.12.1: + resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==} + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + dev: true + + /@webassemblyjs/wasm-parser@1.12.1: + resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==} + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + dev: true + + /@webassemblyjs/wast-printer@1.12.1: + resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@xtuc/long': 4.2.2 + dev: true + + /@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.93.0): + resolution: {integrity: sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==} + engines: {node: '>=14.15.0'} + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + dependencies: + webpack: 5.93.0(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack@5.93.0) + dev: true + + /@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.93.0): + resolution: {integrity: sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==} + engines: {node: '>=14.15.0'} + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + dependencies: + webpack: 5.93.0(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack@5.93.0) + dev: true + + /@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack@5.93.0): + resolution: {integrity: sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==} + engines: {node: '>=14.15.0'} + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + webpack-dev-server: '*' + peerDependenciesMeta: + webpack-dev-server: + optional: true + dependencies: + webpack: 5.93.0(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack@5.93.0) + dev: true + + /@xtuc/ieee754@1.2.0: + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + dev: true + + /@xtuc/long@4.2.2: + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + dev: true + /abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} dev: false + /acorn-import-attributes@1.9.5(acorn@8.8.1): + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + dependencies: + acorn: 8.8.1 + dev: true + /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} @@ -640,6 +1041,12 @@ packages: hasBin: true dev: false + /acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /acorn@8.8.1: resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} engines: {node: '>=0.4.0'} @@ -671,6 +1078,23 @@ packages: dev: false optional: true + /ajv-keywords@3.5.2(ajv@6.12.6): + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + dependencies: + ajv: 6.12.6 + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + /ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -688,6 +1112,11 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -759,8 +1188,8 @@ packages: safer-buffer: 2.1.2 dev: false - /assert-never@1.2.1: - resolution: {integrity: sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==} + /assert-never@1.3.0: + resolution: {integrity: sha512-9Z3vxQ+berkL/JJo0dK+EY3Lp0s3NtSnP3VCLsh5HDcZPrh0M+KQRK5sWhUeyPPH+/RCxZqOxLMR+YC6vlviEQ==} dev: false /async@3.2.4: @@ -774,7 +1203,7 @@ packages: resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==} engines: {node: '>= 10.0.0'} dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.24.9 dev: false /balanced-match@1.0.2: @@ -843,13 +1272,23 @@ packages: dependencies: fill-range: 7.0.1 + /browserslist@4.23.2: + resolution: {integrity: sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001642 + electron-to-chromium: 1.4.829 + node-releases: 2.0.17 + update-browserslist-db: 1.1.0(browserslist@4.23.2) + dev: true + /buffer-equal-constant-time@1.0.1: resolution: {integrity: sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=} dev: false /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: false /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -933,6 +1372,10 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + /caniuse-lite@1.0.30001642: + resolution: {integrity: sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==} + dev: true + /case@1.6.3: resolution: {integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==} engines: {node: '>= 0.8.0'} @@ -944,6 +1387,11 @@ packages: ansi-styles: 4.3.0 supports-color: 7.2.0 + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + /character-parser@2.2.0: resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==} dependencies: @@ -976,6 +1424,11 @@ packages: engines: {node: '>=10'} dev: false + /chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + dev: true + /clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -1005,8 +1458,15 @@ packages: dependencies: restore-cursor: 3.1.0 - /cli-spinners@2.7.0: - resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==} + /cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + restore-cursor: 4.0.0 + dev: true + + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} /cli-table@0.3.11: @@ -1028,6 +1488,15 @@ packages: wrap-ansi: 7.0.0 dev: false + /clone-deep@4.0.1: + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + engines: {node: '>=6'} + dependencies: + is-plain-object: 2.0.4 + kind-of: 6.0.3 + shallow-clone: 3.0.1 + dev: true + /clone-response@1.0.3: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} dependencies: @@ -1043,6 +1512,20 @@ packages: engines: {node: '>=0.10.0'} dev: false + /codemirror@6.0.1(@lezer/common@1.2.1): + resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} + dependencies: + '@codemirror/autocomplete': 6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.28.6)(@lezer/common@1.2.1) + '@codemirror/commands': 6.6.0 + '@codemirror/language': 6.10.2 + '@codemirror/lint': 6.8.1 + '@codemirror/search': 6.5.6 + '@codemirror/state': 6.4.1 + '@codemirror/view': 6.28.6 + transitivePeerDependencies: + - '@lezer/common' + dev: false + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1057,6 +1540,10 @@ packages: hasBin: true dev: false + /colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + dev: true + /colors@1.0.3: resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} engines: {node: '>=0.1.90'} @@ -1067,6 +1554,15 @@ packages: engines: {node: '>=0.1.90'} dev: false + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + + /commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + /concat-map@0.0.1: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} @@ -1077,8 +1573,8 @@ packages: /constantinople@4.0.1: resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} dependencies: - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 dev: false /copyfiles@2.4.1: @@ -1116,6 +1612,48 @@ packages: /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + /crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + dev: false + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /css-loader@7.1.2(webpack@5.93.0): + resolution: {integrity: sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: ^5.27.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + dependencies: + icss-utils: 5.1.0(postcss@8.4.39) + postcss: 8.4.39 + postcss-modules-extract-imports: 3.1.0(postcss@8.4.39) + postcss-modules-local-by-default: 4.0.5(postcss@8.4.39) + postcss-modules-scope: 3.2.0(postcss@8.4.39) + postcss-modules-values: 4.0.0(postcss@8.4.39) + postcss-value-parser: 4.2.0 + semver: 7.6.3 + webpack: 5.93.0(webpack-cli@5.1.4) + dev: true + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + /d@1.0.1: resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} dependencies: @@ -1231,6 +1769,14 @@ packages: safe-buffer: 5.2.1 dev: false + /electron-to-chromium@1.4.829: + resolution: {integrity: sha512-5qp1N2POAfW0u1qGAxXEtz6P7bO1m6gpZr5hdf5ve6lxpLM7MpiM4jIPz7xcrNlClQMafbyUDDWjlIQZ1Mw0Rw==} + dev: true + + /emoji-regex@10.3.0: + resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} + dev: true + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1248,12 +1794,26 @@ packages: once: 1.4.0 dev: false + /enhanced-resolve@5.17.0: + resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + dev: true + /env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} dev: false optional: true + /envinfo@7.13.0: + resolution: {integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==} + engines: {node: '>=4'} + hasBin: true + dev: true + /err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} dev: false @@ -1271,6 +1831,10 @@ packages: engines: {node: '>= 0.4'} dev: false + /es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + dev: true + /es5-ext@0.10.62: resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} engines: {node: '>=0.10'} @@ -1310,6 +1874,11 @@ packages: engines: {node: '>=6'} dev: false + /escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + dev: true + /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1318,6 +1887,14 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + /esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} engines: {node: '>=6'} @@ -1330,6 +1907,23 @@ packages: hasBin: true dev: false + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + /event-emitter@0.3.5: resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} dependencies: @@ -1337,6 +1931,11 @@ packages: es5-ext: 0.10.62 dev: true + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: true + /expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} @@ -1363,6 +1962,10 @@ packages: iconv-lite: 0.4.24 tmp: 0.0.33 + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + /fast-glob@3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} @@ -1373,6 +1976,15 @@ packages: merge2: 1.4.1 micromatch: 4.0.5 + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + dev: true + /fastq@1.13.0: resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} dependencies: @@ -1409,6 +2021,19 @@ packages: dependencies: to-regex-range: 5.0.1 + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + dev: true + /formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -1432,7 +2057,7 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 @@ -1441,7 +2066,7 @@ packages: engines: {node: '>=10'} dependencies: at-least-node: 1.0.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.0 @@ -1502,6 +2127,11 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: false + /get-east-asian-width@1.2.0: + resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + engines: {node: '>=18'} + dev: true + /get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -1530,6 +2160,10 @@ packages: dependencies: is-glob: 4.0.3 + /glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + dev: true + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -1607,6 +2241,9 @@ packages: /graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + /gray-matter@4.0.3: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} @@ -1708,6 +2345,15 @@ packages: dev: false optional: true + /icss-utils@5.1.0(postcss@8.4.39): + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.39 + dev: true + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -1715,6 +2361,15 @@ packages: resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} engines: {node: '>= 4'} + /import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1766,6 +2421,11 @@ packages: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} + /interpret@3.1.1: + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} + dev: true + /ioredis@4.28.5: resolution: {integrity: sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==} engines: {node: '>=6'} @@ -1840,6 +2500,11 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} + /is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + dev: true + /is-lambda@1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} dev: false @@ -1853,6 +2518,13 @@ packages: resolution: {integrity: sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw==} engines: {node: '>=8'} + /is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + dependencies: + isobject: 3.0.1 + dev: true + /is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} @@ -1868,6 +2540,16 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} + /is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + dev: true + + /is-unicode-supported@2.0.0: + resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==} + engines: {node: '>=18'} + dev: true + /is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -1885,6 +2567,20 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + /isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + dev: true + + /jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 18.19.39 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + /js-stringify@1.0.2: resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==} dev: false @@ -1906,17 +2602,25 @@ packages: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} dev: false + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + /jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: universalify: 2.0.0 optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 /jsonwebtoken@8.5.1: resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} @@ -1966,6 +2670,40 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} + /lit-element@4.0.6: + resolution: {integrity: sha512-U4sdJ3CSQip7sLGZ/uJskO5hGiqtlpxndsLr6mt3IQIjheg93UKYeGQjWMRql1s/cXNOaRrCzC2FQwjIwSUqkg==} + dependencies: + '@lit-labs/ssr-dom-shim': 1.2.0 + '@lit/reactive-element': 2.0.4 + lit-html: 3.1.4 + dev: false + + /lit-html@3.1.4: + resolution: {integrity: sha512-yKKO2uVv7zYFHlWMfZmqc+4hkmSbFp8jgjdZY9vvR9jr4J8fH6FUMXhr+ljfELgmjpvlF7Z1SJ5n5/Jeqtc9YA==} + dependencies: + '@types/trusted-types': 2.0.7 + dev: false + + /lit@3.1.4: + resolution: {integrity: sha512-q6qKnKXHy2g1kjBaNfcoLlgbI3+aSOZ9Q4tiGa9bGYXq5RBXxkVTqTIVmP2VWMp29L4GyvCFm8ZQ2o56eUAMyA==} + dependencies: + '@lit/reactive-element': 2.0.4 + lit-element: 4.0.6 + lit-html: 3.1.4 + dev: false + + /loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + /lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} dev: false @@ -2016,6 +2754,14 @@ packages: chalk: 4.1.2 is-unicode-supported: 0.1.0 + /log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + dependencies: + chalk: 5.3.0 + is-unicode-supported: 1.3.0 + dev: true + /lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} @@ -2026,6 +2772,8 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 + dev: false + optional: true /lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} @@ -2100,6 +2848,10 @@ packages: timers-ext: 0.1.7 dev: true + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2114,14 +2866,12 @@ packages: /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - dev: false /mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} dependencies: mime-db: 1.52.0 - dev: false /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} @@ -2240,6 +2990,12 @@ packages: dev: false optional: true + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} dev: false @@ -2249,6 +3005,10 @@ packages: engines: {node: '>= 0.6'} dev: false + /neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + dev: true + /next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: true @@ -2257,16 +3017,15 @@ packages: resolution: {integrity: sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==} engines: {node: '>=10'} dependencies: - semver: 7.3.8 + semver: 7.6.3 dev: false /node-addon-api@5.1.0: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} dev: false - /node-addon-api@7.1.0: - resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} - engines: {node: ^16 || ^18 || >= 20} + /node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} dev: false /node-domexception@1.0.0: @@ -2303,12 +3062,12 @@ packages: dependencies: env-paths: 2.2.1 glob: 7.2.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 make-fetch-happen: 9.1.0 nopt: 5.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.3.8 + semver: 7.6.3 tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: @@ -2317,6 +3076,10 @@ packages: dev: false optional: true + /node-releases@2.0.17: + resolution: {integrity: sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==} + dev: true + /noms@0.0.0: resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} dependencies: @@ -2393,13 +3156,28 @@ packages: bl: 4.1.0 chalk: 4.1.2 cli-cursor: 3.1.0 - cli-spinners: 2.7.0 + cli-spinners: 2.9.2 is-interactive: 1.0.0 is-unicode-supported: 0.1.0 log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 + /ora@8.0.1: + resolution: {integrity: sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==} + engines: {node: '>=18'} + dependencies: + chalk: 5.3.0 + cli-cursor: 4.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.0.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.0 + dev: true + /os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -2421,6 +3199,20 @@ packages: engines: {node: '>=4'} dev: false + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + /p-map@2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} @@ -2441,10 +3233,25 @@ packages: p-finally: 1.0.0 dev: false + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -2532,15 +3339,88 @@ packages: split2: 4.2.0 dev: false + /picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + dev: true + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + /pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} dev: false + /postcss-modules-extract-imports@3.1.0(postcss@8.4.39): + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.39 + dev: true + + /postcss-modules-local-by-default@4.0.5(postcss@8.4.39): + resolution: {integrity: sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + icss-utils: 5.1.0(postcss@8.4.39) + postcss: 8.4.39 + postcss-selector-parser: 6.1.1 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-modules-scope@3.2.0(postcss@8.4.39): + resolution: {integrity: sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.39 + postcss-selector-parser: 6.1.1 + dev: true + + /postcss-modules-values@4.0.0(postcss@8.4.39): + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + dependencies: + icss-utils: 5.1.0(postcss@8.4.39) + postcss: 8.4.39 + dev: true + + /postcss-selector-parser@6.1.1: + resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + + /postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + dev: true + /postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -2740,9 +3620,20 @@ packages: once: 1.4.0 dev: false + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -2794,6 +3685,13 @@ packages: dependencies: resolve: 1.22.1 + /rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + dependencies: + resolve: 1.22.1 + dev: true + /redis-commands@1.7.0: resolution: {integrity: sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==} dev: false @@ -2823,6 +3721,18 @@ packages: engines: {node: '>=0.10.0'} dev: false + /resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true @@ -2844,6 +3754,14 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 + /restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + /retry@0.12.0: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} @@ -2892,6 +3810,15 @@ packages: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} dev: false + /schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + dev: true + /section-matter@1.0.0: resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} engines: {node: '>=4'} @@ -2910,12 +3837,16 @@ packages: hasBin: true dev: false - /semver@7.3.8: - resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} + /semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} hasBin: true + + /serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} dependencies: - lru-cache: 6.0.0 + randombytes: 2.1.0 + dev: true /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} @@ -2933,6 +3864,25 @@ packages: has-property-descriptors: 1.0.2 dev: false + /shallow-clone@3.0.1: + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} + dependencies: + kind-of: 6.0.3 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + /shelljs@0.8.5: resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} engines: {node: '>=4'} @@ -2988,17 +3938,20 @@ packages: dev: false optional: true + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: true + /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: false /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - dev: false /split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} @@ -3022,7 +3975,7 @@ packages: optional: true dependencies: bindings: 1.5.0 - node-addon-api: 7.1.0 + node-addon-api: 7.1.1 prebuild-install: 7.1.2 tar: 6.2.1 optionalDependencies: @@ -3060,6 +4013,11 @@ packages: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} dev: false + /stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + dev: true + /streamsearch@0.1.2: resolution: {integrity: sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==} engines: {node: '>=0.8.0'} @@ -3073,6 +4031,15 @@ packages: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + /string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + dependencies: + emoji-regex: 10.3.0 + get-east-asian-width: 1.2.0 + strip-ansi: 7.1.0 + dev: true + /string_decoder@0.10.31: resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} dev: false @@ -3094,6 +4061,13 @@ packages: dependencies: ansi-regex: 5.0.1 + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + /strip-bom-string@1.0.0: resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} engines: {node: '>=0.10.0'} @@ -3104,12 +4078,32 @@ packages: engines: {node: '>=0.10.0'} dev: false + /style-loader@4.0.0(webpack@5.93.0): + resolution: {integrity: sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.27.0 + dependencies: + webpack: 5.93.0(webpack-cli@5.1.4) + dev: true + + /style-mod@4.1.2: + resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} + dev: false + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} dependencies: has-flag: 4.0.0 + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + /supports-hyperlinks@2.3.0: resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} engines: {node: '>=8'} @@ -3122,6 +4116,11 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + /tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + /tar-fs@2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} dependencies: @@ -3162,6 +4161,41 @@ packages: supports-hyperlinks: 2.3.0 dev: false + /terser-webpack-plugin@5.3.10(webpack@5.93.0): + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.31.3 + webpack: 5.93.0(webpack-cli@5.1.4) + dev: true + + /terser@5.31.3: + resolution: {integrity: sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.12.1 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + /threads@1.7.0: resolution: {integrity: sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==} dependencies: @@ -3416,6 +4450,23 @@ packages: engines: {node: '>=8'} dev: false + /update-browserslist-db@1.1.0(browserslist@4.23.2): + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.23.2 + escalade: 3.1.2 + picocolors: 1.0.1 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -3431,6 +4482,18 @@ packages: engines: {node: '>=0.10.0'} dev: false + /w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + dev: false + + /watchpack@2.4.1: + resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==} + engines: {node: '>=10.13.0'} + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + dev: true + /wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: @@ -3445,6 +4508,94 @@ packages: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: false + /webpack-cli@5.1.4(webpack@5.93.0): + resolution: {integrity: sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==} + engines: {node: '>=14.15.0'} + hasBin: true + peerDependencies: + '@webpack-cli/generators': '*' + webpack: 5.x.x + webpack-bundle-analyzer: '*' + webpack-dev-server: '*' + peerDependenciesMeta: + '@webpack-cli/generators': + optional: true + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + dependencies: + '@discoveryjs/json-ext': 0.5.7 + '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.93.0) + '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.93.0) + '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack@5.93.0) + colorette: 2.0.20 + commander: 10.0.1 + cross-spawn: 7.0.3 + envinfo: 7.13.0 + fastest-levenshtein: 1.0.16 + import-local: 3.1.0 + interpret: 3.1.1 + rechoir: 0.8.0 + webpack: 5.93.0(webpack-cli@5.1.4) + webpack-merge: 5.10.0 + dev: true + + /webpack-merge@5.10.0: + resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} + engines: {node: '>=10.0.0'} + dependencies: + clone-deep: 4.0.1 + flat: 5.0.2 + wildcard: 2.0.1 + dev: true + + /webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + dev: true + + /webpack@5.93.0(webpack-cli@5.1.4): + resolution: {integrity: sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.8.1 + acorn-import-attributes: 1.9.5(acorn@8.8.1) + browserslist: 4.23.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.0 + es-module-lexer: 1.5.4 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.10(webpack@5.93.0) + watchpack: 2.4.1 + webpack-cli: 5.1.4(webpack@5.93.0) + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + dev: true + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: @@ -3464,8 +4615,6 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: false - optional: true /wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} @@ -3479,13 +4628,17 @@ packages: dependencies: string-width: 4.2.3 + /wildcard@2.0.1: + resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} + dev: true + /with@7.0.2: resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} engines: {node: '>= 10.0.0'} dependencies: - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 - assert-never: 1.2.1 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 + assert-never: 1.3.0 babel-walk: 3.0.0-canary-5 dev: false @@ -3508,8 +4661,8 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - /ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + /ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -3553,6 +4706,7 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: false /yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} diff --git a/src/app/http/controllers/Dash2.controller.ts b/src/app/http/controllers/Dash2.controller.ts new file mode 100644 index 0000000..e239588 --- /dev/null +++ b/src/app/http/controllers/Dash2.controller.ts @@ -0,0 +1,169 @@ +import { + Config, + Controller, + HTTPError, + HTTPStatus, + Inject, + Injectable, + Logging, + Maybe, + ResponseObject, + Routing, + SecurityContext, + view, +} from '@extollo/lib' +import {User} from '../../models/User.model' +import {one} from '@extollo/lib/lib/http/response/api' +import {ResourceAction, ResourceConfiguration} from '../../cobalt' + +@Injectable() +export class Dash2 extends Controller { + @Inject() + protected readonly logging!: Logging + + @Inject() + protected readonly routing!: Routing + + @Inject() + protected readonly security!: SecurityContext + + @Inject() + protected readonly config!: Config + + public async index(): Promise { + return view('dash2:home', { + title: 'Home', + ...(await this.appData()), + }) + } + + public async cobalt(): Promise { + return view('dash2:cobalt', { + ...(await this.appData()), + }) + } + + public async cobaltSettings(): Promise { + const layout = this.request.safe('layoutname').string() + const params: Record = this.request.input('params') as any || {} + + if ( layout === 'resource:form' && typeof params.resource === 'string' ) { + return this.getResourceFormSettings(params.resource, params) + } + + if ( layout === 'resource:list' && typeof params.resource === 'string' ) { + return this.getResourceListSettings(params.resource, params) + } + + return one({ + layout, + params, + }) + } + + public async cobaltResourceList(): Promise { + return view('dash2:cobalt', { + ...(await this.appData()), + layout: 'resource:list', + params: { resource: this.request.safe('key').string() }, + }) + } + + private async getResourceListSettings(resourceName: string, params: Record): Promise { + const config = this.getResourceConfigOrFail(resourceName) + + return one({ + loadActions: [ + { + target: { + sourceName: `resource:list:${resourceName}`, + }, + action: { + type: 'route', + method: 'GET', + route: `/dash/cobalt/resource/${resourceName}`, + successKey: 'success', + resultKey: 'data', + }, + }, + ], + layoutChildren: [ + { + component: 'table', + config: { + sourceName: `resource:list:${resourceName}`, + rowKey: 'records', + fields: config.fields, + }, + }, + ], + }) + } + + private async getResourceFormSettings(resourceName: string, params: Record): Promise { + const config = this.getResourceConfigOrFail(resourceName) + + return one({ + layoutChildren: [ + { + component: 'card', + config: { + display: config.display.singular, + }, + layoutChildren: [ + { + component: 'form', + sourceName: `resource:form:${resourceName}`, + layoutChildren: config.fields + .filter(f => !f.hideOn?.form) + .map(f => ({ + component: 'field', + config: f, + })) + }, + ], + }, + ], + }) + } + + public async appData() { + const resourceConfigs = this.config.get('cobalt.resources', []) as ResourceConfiguration[] + const user = this.security.user() as User + return { + appData: { + appUrl: this.routing.getAppUrl().toRemote, + user: { + firstName: user.firstName, + lastName: user.lastName, + username: user.username, + photoUrl: user.photoUrl, + initials: user.initials, + }, + }, + resources: resourceConfigs + .filter(c => c.supportedActions.includes(ResourceAction.read)) + .map(c => ({ + display: c.display.plural, + href: `/dash2/cobalt/resource/${c.key}/list`, + })), + } + } + + protected getResourceConfigOrFail(key: string): ResourceConfiguration { + const config = this.getResourceConfig(key) + if ( !config ) { + throw new HTTPError(HTTPStatus.NOT_FOUND) + } + return config + } + + protected getResourceConfig(key: string): Maybe { + const configs = this.config.get('cobalt.resources') as ResourceConfiguration[] + for ( const config of configs ) { + if ( config.key === key ) { + return config + } + } + } +} diff --git a/src/app/http/routes/dash.routes.ts b/src/app/http/routes/dash.routes.ts index bcab1c0..97a4328 100644 --- a/src/app/http/routes/dash.routes.ts +++ b/src/app/http/routes/dash.routes.ts @@ -3,6 +3,7 @@ import {Dash} from '../controllers/Dash.controller' import CobaltMiddleware from '../middlewares/Cobalt.middleware' import {ResourceAPI} from '../controllers/cobalt/ResourceAPI.controller' import {Interface} from '../controllers/cobalt/Interface.controller' +import {Dash2} from '../controllers/Dash2.controller' const parseKey = (request: Request) => right(request.safe('key').string()) const parseId = (request: Request) => { @@ -10,6 +11,24 @@ const parseId = (request: Request) => { return right(request.input('id') as number|string) } +Route + .group('/dash2', () => { + Route.get('/') + .calls(Dash2, d => d.index) + + Route.get('/cobalt') + .calls(Dash2, d => d.cobalt) + + Route.get('/cobalt/settings') + .calls(Dash2, d => d.cobaltSettings) + + Route.get('/cobalt/resource/:key/list') + .calls(Dash2, d => d.cobaltResourceList) + }) + .pre(SessionAuthMiddleware) + .pre(AuthRequiredMiddleware) + .pre(CobaltMiddleware) + Route .group('/dash', () => { Route.get('/') diff --git a/src/app/models/User.model.ts b/src/app/models/User.model.ts index 7c21dbb..e2c5808 100644 --- a/src/app/models/User.model.ts +++ b/src/app/models/User.model.ts @@ -18,6 +18,15 @@ export class User extends ORMUser { return `${this.routing.getAppUrl().toRemote}/pub/${this.username}` } + get initials(): string { + return `${this.firstName || ''} ${this.lastName || ''}` + .trim() + .split(/\s+/) + .map(s => s[0]) + .join('') + .toUpperCase() + } + async toWebfinger(): Promise { const host = new URL(this.routing.getAppUrl().toRemote).host return { diff --git a/src/app/resources/assets/app/App.ts b/src/app/resources/assets/app/App.ts index 864ab0e..9ea946e 100644 --- a/src/app/resources/assets/app/App.ts +++ b/src/app/resources/assets/app/App.ts @@ -56,6 +56,6 @@ export class App = SubscriberFunction | ComplexSubscriber */ export type Unsubscribe = { unsubscribe: () => void } +export interface IBehaviorSubject { + /** + * Register a new subscription to this subject. + * @param {Subscription} subscriber + * @return Unsubscribe + */ + subscribe(subscriber: Subscription): Unsubscribe + + /** + * Cast this subject to a promise, which resolves on the output of the next value. + * @return Promise + */ + toPromise(): Promise + + /** + * Push a new value to this subject. The promise resolves when all subscribers have been pushed to. + * @param val + * @return Promise + */ + next(val: T): Promise + + /** + * Push the given array of values to this subject in order. + * The promise resolves when all subscribers have been pushed to for all values. + * @param {Array} vals + * @return Promise + */ + push(vals: T[]): Promise + + /** + * Mark this subject as complete. + * The promise resolves when all subscribers have been pushed to. + * @param [finalValue] - optionally, a final value to set + * @return Promise + */ + complete(finalValue?: T): Promise + + /** + * Get the current value of this subject. + */ + value(): T | undefined + + /** + * True if this subject is marked as complete. + * @return boolean + */ + isComplete(): boolean +} + /** * A stream-based state class. */ -export class BehaviorSubject { +export class BehaviorSubject implements IBehaviorSubject { /** * Subscribers to this subject. * @type Array @@ -77,6 +126,12 @@ export class BehaviorSubject { */ protected hasPush = false + constructor( + initialValue?: T + ) { + this.currentValue = initialValue + } + /** * Register a new subscription to this subject. * @param {Subscription} subscriber @@ -210,3 +265,76 @@ export class BehaviorSubject { return this.subjectIsComplete } } + +/** A behavior subject that targets a sub-value of a parent subject. */ +export class MappedBehaviorSubject implements IBehaviorSubject { + constructor( + private parent: IBehaviorSubject, + private key: TKey, + ) {} + + /** + * True if this subject has been marked complete. + * @type boolean + */ + protected subjectIsComplete = false + + async complete(finalValue?: TBase[TKey]): Promise { + this.subjectIsComplete = true + + if ( finalValue ) { + const current = this.parent.value() + if ( !current ) { + throw new Error('Cannot set mapped behavior subject final value: parent value is undefined') + } + + current[this.key] = finalValue + return this.parent.complete(current) + } + + return this.parent.complete() + } + + isComplete(): boolean { + return this.subjectIsComplete || this.parent.isComplete() + } + + async next(val: TBase[TKey]): Promise { + const current = this.parent.value() + if ( !current ) { + throw new Error('Cannot set mapped behavior subject: parent value is undefined') + } + + current[this.key] = val + return this.parent.next(current) + } + + async push(vals: TBase[TKey][]): Promise { + for ( const val of vals ) { + await this.next(val) + } + } + + subscribe(subscriber: Subscription): Unsubscribe { + if ( typeof subscriber === 'function' ) { + return this.parent + .subscribe(val => subscriber(val[this.key])) + } + + return this.parent + .subscribe({ + next: (val: TBase) => subscriber.next?.(val[this.key]), + error: error => subscriber.error?.(error), + complete: (val: TBase|undefined) => subscriber.complete?.(val?.[this.key]), + }) + } + + async toPromise(): Promise { + const val = await this.parent.toPromise() + return val[this.key] + } + + value(): TBase[TKey] | undefined { + return this.parent.value()?.[this.key] + } +} diff --git a/src/app/resources/assets/app/Component.ts b/src/app/resources/assets/app/Component.ts deleted file mode 100644 index bf784ba..0000000 --- a/src/app/resources/assets/app/Component.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {app, BaseApp} from './App' - -export abstract class Component extends HTMLElement { - protected app(): BaseApp|undefined { - return app() - } - - constructor() { - super() - this.initialize() - } - - protected initialize(): void {} -} diff --git a/src/app/resources/assets/app/cobalt/DataPlane.ts b/src/app/resources/assets/app/cobalt/DataPlane.ts new file mode 100644 index 0000000..b1c47b4 --- /dev/null +++ b/src/app/resources/assets/app/cobalt/DataPlane.ts @@ -0,0 +1,44 @@ +import {BehaviorSubject, IBehaviorSubject, MappedBehaviorSubject} from '../BehaviorSubject' + +export type DataSource = { + getField(fieldPath: string): IBehaviorSubject + getAll(): IBehaviorSubject> +} + +export class RecordDataSource implements DataSource { + private base: BehaviorSubject> = new BehaviorSubject({}) + + getField(field: string): IBehaviorSubject { + return new MappedBehaviorSubject, typeof field>(this.base, field) + } + + getAll(): IBehaviorSubject> { + return this.base + } +} + +export class DataPlane { + private sources: Record = {} + + public ensureSource(name: string, source?: DataSource) { + if ( !this.hasSource(name) ) { + this.addSource(name, source || new RecordDataSource()) + } + } + + public addSource(name: string, source: DataSource) { + this.sources[name] = source + } + + public hasSource(name: string): boolean { + return Boolean(this.sources[name]) + } + + public getField(source: string, field: string): IBehaviorSubject | undefined { + return this.sources[source]?.getField?.(field) + } + + public getAll(source: string): IBehaviorSubject> | undefined { + return this.sources[source]?.getAll?.() + } +} diff --git a/src/app/resources/assets/app/cobalt/form/Code.ts b/src/app/resources/assets/app/cobalt/form/Code.ts new file mode 100644 index 0000000..96831c5 --- /dev/null +++ b/src/app/resources/assets/app/cobalt/form/Code.ts @@ -0,0 +1,125 @@ +import {css, html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {Ex} from '../../util' +import uuid = Ex.uuid +import {EditorView, basicSetup} from 'codemirror' +import {keymap, placeholder} from '@codemirror/view' +import {EditorState} from '@codemirror/state' +import {indentWithTab} from '@codemirror/commands' +import * as langHtml from '@codemirror/lang-html' + +export class CodeChangeEvent extends Event { + constructor(public readonly content: string, baseEventContent?: EventInit) { + super('code-change', baseEventContent) + } +} + +@customElement('cobalt-code') +export class Code extends LitElement { + static override styles = css` + .editor-container { + width: calc(100% - 10px); + background: #ffffff; + padding: 5px; + border: 1px solid #999; + border-radius: 7px; + } + ` + + @property() + initialValue?: string + + @property() + syntax?: string + + @property() + placeholder?: string + + @property() + required?: boolean + + @property() + disabled?: boolean + + @property() + label?: string + + @property() + helpText?: string + + private editorId: string = 'cobalt-code-' + uuid() + private value: string = '' + private editor?: EditorView + + override connectedCallback() { + super.connectedCallback() + + requestAnimationFrame(() => { + const el = this.getEditorContainer() + if ( !el ) { + console.warn('Could not initialize cobalt-code: could not get editor container element') + return + } + + this.initializeEditor(el) + }) + } + + override render() { + const helpText = this.helpText ? html`${this.helpText}` : html`` + + return html` + ${this.label}${this.required ? '*' : ''} +
+ ${helpText} + ` + } + + private initializeEditor(parent: HTMLElement) { + this.value = this.initialValue || '' + + const extensions = [ + basicSetup, + keymap.of([indentWithTab]), + EditorView.updateListener.of((e) => { + if ( e.docChanged ) { + this.dispatchEvent(new CodeChangeEvent(this.editor!.state.doc.toString())) + } + }) + ] + + const lang = this.getLangPlugin() + if ( lang ) { + extensions.push(lang) + } + + if ( this.placeholder ) { + extensions.push(placeholder(this.placeholder)) + } + + if ( this.disabled ) { + extensions.push(EditorState.readOnly.of(true)) + } + + this.editor = new EditorView({ + extensions, + parent, + doc: this.value, + }) + } + + private getLangPlugin() { + if ( this.syntax === 'html' ) { + return langHtml.html() + } + + return undefined + } + + private getEditorContainer(): HTMLElement | null { + return (this.shadowRoot || this).querySelector(`#${this.editorId}`) + } +} diff --git a/src/app/resources/assets/app/cobalt/form/Field.ts b/src/app/resources/assets/app/cobalt/form/Field.ts new file mode 100644 index 0000000..4f147a3 --- /dev/null +++ b/src/app/resources/assets/app/cobalt/form/Field.ts @@ -0,0 +1,285 @@ +import {css, html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {FieldType, SelectOptions} from '../types' +import type {FieldDefinition} from '../types' +import {DataPlane} from '../DataPlane' +import {IBehaviorSubject, Unsubscribe} from '../../BehaviorSubject' +import {Ex} from '../../util' +import uuid = Ex.uuid +import {CodeChangeEvent} from './Code' +import {FormState} from './State' + +export const inferFieldValue = (value: string, type: FieldType): unknown => { + if ( type === FieldType.number ) { + if ( !value ) { + return undefined + } + + return parseFloat(String(value)) + } + + if ( type === FieldType.integer ) { + if ( !value ) { + return undefined + } + + return parseInt(String(value), 10) + } + + if ( type === FieldType.date ) { + const date = new Date(`${value} 00:00:00`) + if ( isNaN(date.getTime()) ) { + return undefined + } + return date + } + + return value +} + +@customElement('cobalt-form-field') +export class Field extends LitElement { + static override styles = css` + .field-wrapper { + padding: 7px; + } + ` + + @property({ attribute: false }) + plane?: DataPlane + + @property({ attribute: false }) + config?: FieldDefinition + + @property({ attribute: false }) + parentContext: Record = {} + + private value?: IBehaviorSubject + private selectOptionMap?: Record + private dirty: boolean = false + private formStateSub?: Unsubscribe + private formState?: FormState + + private getSourceName(): string|undefined { + return this.parentContext.sourceName + } + + private getStateSourceName(): string|undefined { + const sourceName = this.getSourceName() + return sourceName ? `${sourceName}-state` : undefined + } + + override render() { + return html` +
+ ${this.renderField()} +
+ ` + } + + renderField() { + const sourceName = this.getSourceName() + const stateSourceName = this.getStateSourceName() + + if ( !this.config || !sourceName || !this.plane ) { + return html`...` + } + + if ( !this.value ) { + this.value = this.plane.getField(sourceName, this.config.key) + } + + if ( stateSourceName && !this.formStateSub ) { + this.formStateSub = this.plane + .getAll(stateSourceName) + ?.subscribe(state => { + this.formState = state as FormState + this.requestUpdate() + }) + } + + if ( this.isInputType(this.config.type) ) { + return html` + + ${this.renderErrors()} + ` + } + + if ( this.config.type === FieldType.textarea ) { + return html` + + ${this.renderErrors()} + ` + } + + if ( this.config.type === FieldType.bool ) { + return html` + + ${this.config.display} + + ${this.renderErrors()} + ` + } + + if ( this.config.type === FieldType.select && 'options' in this.config ) { + if ( !this.selectOptionMap ) { + this.buildSelectOptionMap() + } + + return html` + + ${this.config.options.map(o => this.renderSelectOption(o))} + + ${this.renderErrors()} + ` + } + + if ( this.config.type === FieldType.html ) { + return html` + + ${this.renderErrors()} + ` + } + + return html` + Invalid field configuration: ${this.config.type} + ` + } + + private buildSelectOptionMap() { + if ( !this.config || this.config.type !== FieldType.select || !('options' in this.config) ) { + return + } + + this.selectOptionMap = {} + for ( const opt of this.config.options ) { + if ( !opt.uuid ) { + opt.uuid = uuid() + } + + this.selectOptionMap[opt.uuid] = opt.value + } + } + + private renderSelectOption(option: SelectOptions[0]) { + return html` + + ${option.display} + + ` + } + + private renderErrors() { + if ( !this.formState?.errors?.[this.config!.key]?.length || !this.dirty ) { + return html`` + } + + const errorDisplays = this.formState!.errors[this.config!.key]! + .map(str => html`${str}`) + + return html`
${errorDisplays}
` + } + + private mapInputType(type: FieldType): string { + if ( type === FieldType.email ) { + return 'email' + } + + if ( type === FieldType.number || type === FieldType.integer ) { + return 'number' + } + + if ( type === FieldType.date ) { + return 'date' + } + + return 'text' + } + + private isInputType(type: FieldType): boolean { + return [ + FieldType.text, + FieldType.email, + FieldType.number, + FieldType.integer, + FieldType.date, + ].includes(type) + } + + private setValue(event: Event) { + const newValue = this.getFieldValue(event) + this.dirty = newValue !== this.value?.value() + this.value?.next(newValue) + } + + private getFieldValue(event: Event) { + const input = (this.shadowRoot || this).querySelector(`#field-${this.getSourceName()!}-${this.config!.key}`) + + if ( this.config!.type === FieldType.bool ) { + return !!(input as any)?.checked + } + + if ( this.config!.type === FieldType.select ) { + const uuid = (input as any)?.value + return this.selectOptionMap?.[uuid] + } + + if ( this.config!.type === FieldType.html && event instanceof CodeChangeEvent ) { + return event.content + } + + return inferFieldValue((input as any)?.value, this.config!.type) + } +} diff --git a/src/app/resources/assets/app/cobalt/form/Form.ts b/src/app/resources/assets/app/cobalt/form/Form.ts new file mode 100644 index 0000000..66b3dc4 --- /dev/null +++ b/src/app/resources/assets/app/cobalt/form/Form.ts @@ -0,0 +1,86 @@ +import {css, html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import type {FieldDefinition, FormConfig} from '../types' +import {DataPlane} from '../DataPlane' +import {Ex} from '../../util' +import uuid = Ex.uuid +import {LayoutSpec} from '../layout/Layout' +import {FormStateManager} from './State' + +@customElement('cobalt-form') +export class Form extends LitElement { + static override styles = css`` + + @property({ attribute: false }) + plane?: DataPlane + + @property({ attribute: false }) + config?: FormConfig + + @property({ attribute: false }) + layoutChildren?: LayoutSpec[] + + @property({ attribute: false }) + parentContext: Record = {} + + private stateManager?: FormStateManager + + getContextIdentifier(): string { + return this.config?.sourceName || uuid() + } + + getStateContextIdentifier(): string { + return `${this.getContextIdentifier()}-state` + } + + override render() { + if ( !this.config || !this.plane ) { + return html`Loading...` + } + + this.plane.ensureSource(this.getContextIdentifier()) + this.plane.ensureSource(this.getStateContextIdentifier()) + + if ( !this.stateManager ) { + this.stateManager = new FormStateManager( + this.getFields(this.layoutChildren || []), + this.getContextIdentifier(), + this.getStateContextIdentifier(), + this.plane, + ) + } + + const context = { + ...this.parentContext, + sourceName: this.getContextIdentifier(), + } + + return html` + + ` + } + + private getFields(layout: LayoutSpec[], fields: FieldDefinition[] = []): FieldDefinition[] { + for ( const item of layout ) { + if ( item.component === 'field' ) { + if ( item.config ) { + fields.push(item.config as FieldDefinition) + } + + continue + } + + if ( item.component === 'form' || !item.layoutChildren ) { + continue + } + + this.getFields(item.layoutChildren, fields) + } + + return fields + } +} diff --git a/src/app/resources/assets/app/cobalt/form/State.ts b/src/app/resources/assets/app/cobalt/form/State.ts new file mode 100644 index 0000000..bb02b5b --- /dev/null +++ b/src/app/resources/assets/app/cobalt/form/State.ts @@ -0,0 +1,108 @@ +import {DataPlane, DataSource, RecordDataSource} from '../DataPlane' +import {FieldDefinition, FieldType} from '../types' +import {Unsubscribe} from '../../BehaviorSubject' + +export type FormErrors = Record + +export type FormState = { + dirty: boolean + valid: boolean + errors: FormErrors +} + +export class FormStateManager { + private sub?: Unsubscribe + + constructor( + private readonly fields: FieldDefinition[], + private readonly formSource: string, + private readonly stateSource: string, + private readonly plane: DataPlane, + ) { + this.sub = this.plane.getAll(this.formSource) + ?.subscribe(formData => + this.rebuildState(formData)) + + this.rebuildState() + } + + private rebuildState(formData?: Record) { + if ( !formData ) { + this.plane + .getAll(this.stateSource) + ?.next({ dirty: false, errors: {} }) + + return + } + + const errors = this.updateValidation(formData) + this.plane + .getAll(this.stateSource) + ?.next({ + dirty: true, + valid: !Object.keys(errors).length, + errors, + }) + } + + private updateValidation(formData: Record): FormErrors { + const errors: FormErrors = {} + + for ( const field of this.fields ) { + const value = formData[field.key] + + if ( + field.required + && !value + && ( + ( + field.type !== FieldType.integer + && field.type !== FieldType.number + ) + || value !== 0 + ) + ) { + if ( !errors[field.key] ) errors[field.key] = [] + errors[field.key]!.push(`${field.display} is required.`) + } + + if ( + value + && field.type === FieldType.email + && !(/^\S+@\S+\.\S+$/.test(String(value))) + ) { + if ( !errors[field.key] ) errors[field.key] = [] + errors[field.key]!.push(`${field.display} must be a valid email address.`) + } + + if ( + (value || value === 0) + && ( + field.type === FieldType.integer + || field.type === FieldType.number + ) + && ( + typeof value !== 'number' + || isNaN(value) + ) + ) { + if ( !errors[field.key] ) errors[field.key] = [] + errors[field.key]!.push(`${field.display} must be a valid ${field.type}`) + } + + if ( + value + && field.type === FieldType.date + && ( + !(value instanceof Date) + || isNaN(value.getTime()) + ) + ) { + if ( !errors[field.key] ) errors[field.key] = [] + errors[field.key]!.push(`${field.display} must be a valid date`) + } + } + + return errors + } +} diff --git a/src/app/resources/assets/app/cobalt/host/Host.ts b/src/app/resources/assets/app/cobalt/host/Host.ts new file mode 100644 index 0000000..1c1dc46 --- /dev/null +++ b/src/app/resources/assets/app/cobalt/host/Host.ts @@ -0,0 +1,226 @@ +import {html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {DataPlane} from '../DataPlane' +import {Task} from '@lit/task' +import {LayoutSpec} from '../layout/Layout' +import {CobaltAction, CobaltActionResult, LoadAction} from '../types' + +export type HostSettings = { + parentContext?: Record, + layoutChildren: LayoutSpec[], + loadActions?: LoadAction[], +} + +@customElement('cobalt-host') +export class Host extends LitElement { + @property() + settingsendpoint?: string + + @property() + layoutname?: string + + @property({ attribute: false }) + plane?: DataPlane + + @property() + paramsSourceName: string = 'params' + + @property() + initialParamsSourceName?: string + + @property({ attribute: false }) + initialParams?: Record + + @property() + initialparamsjson?: string + + private settings?: HostSettings + + private renderTask = new Task(this, { + args: () => [this.settingsendpoint, this.layoutname, this.plane, this.initialParams, this.initialparamsjson], + task: async ([settingsEndpoint, layoutName,,], options) => { + if ( this.settings ) { + return this.settings + } + + const plane = this.getDataPlane() + plane.ensureSource(this.paramsSourceName) + + const params = this.getParams() + await plane.getAll(this.paramsSourceName)!.next(params) + + const searchQuery = new URLSearchParams({ + layoutname: `${layoutName || ''}`, + params: JSON.stringify(params), + }) + + const settingsUrl = `${settingsEndpoint}?${searchQuery}` + const response = await fetch(settingsUrl) + const json: any = await response.json() + + if ( !json.success || !json.data?.layoutChildren ) { + return + } + + this.settings = json.data + await this.runLoadActions() + return this.settings + } + }) + + override render() { + return this.renderTask.render({ + pending: () => html`...`, + complete: (settings?: HostSettings) => { + if ( !settings ) { + return html`...` + } + + return html` + + ` + }, + error: e => html`Error: ${e}`, + }) + } + + private getParams(): Record { + let params: Record = {} + + // Merge params from the data plane, if we have any: + if ( this.initialParamsSourceName ) { + const planeParams = this.getDataPlane() + .getAll(this.initialParamsSourceName) + ?.value() + + if ( planeParams ) { + params = {...params, ...planeParams} + } + } + + // Merge params passed in via JSON, if we have any: + if ( this.initialparamsjson ) { + const jsonParams = JSON.parse(this.initialparamsjson) + params = {...params, ...jsonParams} + } + + // Merge params passed in directly, if we have any: + if ( this.initialParams ) { + params = {...params, ...this.initialParams} + } + + return params + } + + private dp?: DataPlane + private getDataPlane(): DataPlane { + return this.plane || this.dp || (this.dp = new DataPlane) + } + + private async runLoadActions(): Promise { + if ( !this.settings?.loadActions ) { + return + } + + await Promise.all( + this.settings + .loadActions + .map(load => this.runLoadAction(load)) + ) + } + + private async runLoadAction(load: LoadAction): Promise { + const result = await this.runAction(load.action) + if ( !result.success ) { + console.error('Load action failed!', result) + return // fixme: handle this better + } + + console.log(`setting load data for ${load.target.sourceName}->${load.target.fieldName || '@'}`, result) + + this.getDataPlane().ensureSource(load.target.sourceName) + + const target = load.target.fieldName + ? this.getDataPlane().getField(load.target.sourceName, load.target.fieldName) + : this.getDataPlane().getAll(load.target.sourceName) + + await target?.next(result.result) + } + + private async runAction(action: CobaltAction): Promise { + try { + const params = await this.gatherActionParams(action) + + let result: any + if ( action.type === 'route' ) { + result = await this.runRouteAction(action, params) + } + + if ( action.successKey && !result[action.successKey] ) { + return { + success: false, + message: result.message || undefined, + error: result, + } + } + + return { + success: true, + message: result.message || undefined, + result: action.resultKey ? result[action.resultKey] : result, + } + } catch (e) { + return { + success: false, + error: e, + } + } + } + + private async runRouteAction(action: CobaltAction & { type: 'route' }, params: Record): Promise { + let body: undefined|string = undefined + let route = action.route + if ( action.method === 'GET' || action.method === 'HEAD' ) { + route = `${route}?${new URLSearchParams({ params: JSON.stringify(params) })}` + } else { + body = JSON.stringify({ params }) + } + + const response = await fetch(route, { + method: action.method, + body, + }) + + return await response.json() + } + + private async gatherActionParams(action: CobaltAction): Promise> { + let params: Record = {} + + if ( action.data ) { + params = {...params, ...action.data} + } + + if ( action.gather ) { + const plane = this.getDataPlane() + for ( const key in action.gather ) { + const ref = action.gather[key] + if ( !ref ) { + continue + } + + if ( ref.fieldName ) { + params[key] = plane.getField(ref.sourceName, ref.fieldName)?.value() + } else { + params[key] = plane.getAll(ref.sourceName)?.value() + } + } + } + + return params + } +} diff --git a/src/app/resources/assets/app/cobalt/index.ts b/src/app/resources/assets/app/cobalt/index.ts new file mode 100644 index 0000000..69a1950 --- /dev/null +++ b/src/app/resources/assets/app/cobalt/index.ts @@ -0,0 +1,10 @@ +export * from './form/Form' +export * from './form/Field' +export * from './form/Code' + +export * from './list/Table' + +export * from './layout/components' +export * from './layout/Layout' + +export * from './host/Host' diff --git a/src/app/resources/assets/app/cobalt/layout/Layout.ts b/src/app/resources/assets/app/cobalt/layout/Layout.ts new file mode 100644 index 0000000..556a53e --- /dev/null +++ b/src/app/resources/assets/app/cobalt/layout/Layout.ts @@ -0,0 +1,180 @@ +import {html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {DataPlane} from '../DataPlane' +import {FieldType} from '../types' + +export type LayoutSpec = { + component: string + config?: unknown + layoutChildren?: LayoutSpec[] +} + +export type LayoutContext = { + plane: DataPlane + config: any + parentContext: Record + layoutChildren?: LayoutSpec[] +} + +export type LayoutComponent = { + name: string + factory: (context: LayoutContext) => unknown, // fixme +} + +@customElement('cobalt-layout') +export class Layout extends LitElement { + @property({ attribute: false }) + plane?: DataPlane + + @property({ attribute: false }) + parentContext: Record = {} + + @property({ attribute: false }) + layoutChildren?: LayoutSpec[] + + private registry: Record = { + row: { + name: 'row', + factory: context => html` + + ` + }, + col: { + name: 'col', + factory: context => html` + + ` + }, + group: { + name: 'group', + factory: context => html` + + ` + }, + card: { + name: 'card', + factory: context => html` + + ` + }, + form: { + name: 'form', + factory: context => html` + + ` + }, + field: { + name: 'field', + factory: context => html` + + ` + }, + table: { + name: 'table', + factory: context => html` + + ` + }, + } + + private spec: LayoutSpec = { + component: 'card', + config: { + display: 'People', + }, + layoutChildren: [ + { + component: 'table', + config: { + sourceName: 'test-list-1', + rowKey: 'rows', + fields: [ + { + key: 'first_name', + display: 'First Name', + type: FieldType.text, + }, + { + key: 'last_name', + display: 'Last Name', + type: FieldType.text, + sort: 'asc', + }, + ], + }, + }, + ], + } + + override render() { + if ( !this.layoutChildren ) { + return this.makeComponent(this.spec) + // return html`...` + } + + return html` + ${this.layoutChildren.map(child => this.makeComponent(child))} + ` + } + + private makeComponent(spec: LayoutSpec) { + const factory = this.registry[spec.component] + if ( !factory ) { + console.error('Cannot find component factory for spec:', spec) + return html`...` + } + + const context: LayoutContext = { + plane: this.getDataPlane(), + config: spec.config || {}, + parentContext: this.parentContext, + layoutChildren: spec.layoutChildren, + } + + return factory.factory(context) + } + + private dp?: DataPlane + private getDataPlane(): DataPlane { + if ( this.plane ) { + return this.plane + } + + if ( this.dp ) { + return this.dp + } + + return this.dp = new DataPlane() + } +} diff --git a/src/app/resources/assets/app/cobalt/layout/components/Card.ts b/src/app/resources/assets/app/cobalt/layout/components/Card.ts new file mode 100644 index 0000000..8ac0ced --- /dev/null +++ b/src/app/resources/assets/app/cobalt/layout/components/Card.ts @@ -0,0 +1,67 @@ +import {css, html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {DataPlane} from '../../DataPlane' +import {LayoutSpec} from '../Layout' + +export type CardConfig = { + display?: string +} + +@customElement('cobalt-card') +export class Card extends LitElement { + static override styles = css` + .card-wrapper { + display: flex; + flex-direction: row; + justify-content: center; + } + + .card { + background: #f0f0f0; + border: 1px solid #ccc; + border-radius: 5px; + width: 50%; + box-shadow: 3px 3px 8px 0 rgba(156, 156, 156, 1); + } + + .label { + padding: 7px; + border-bottom: 1px solid #ddd; + font-size: 1.2em; + font-weight: 500; + } + + .content { + padding: 7px; + } + ` + + @property({ attribute: false }) + config: CardConfig = {} + + @property({ attribute: false }) + plane?: DataPlane + + @property({ attribute: false }) + layoutChildren?: LayoutSpec[] + + @property({ attribute: false }) + parentContext: Record = {} + + override render() { + return html` +
+
+
${this.config.display || ''}
+
+ +
+
+
+ ` + } +} diff --git a/src/app/resources/assets/app/cobalt/layout/components/Column.ts b/src/app/resources/assets/app/cobalt/layout/components/Column.ts new file mode 100644 index 0000000..d99a8b0 --- /dev/null +++ b/src/app/resources/assets/app/cobalt/layout/components/Column.ts @@ -0,0 +1,50 @@ +import {css, html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {DataPlane} from '../../DataPlane' +import {LayoutSpec} from '../Layout' + +@customElement('cobalt-col') +export class Column extends LitElement { + static override styles = css` + .cobalt-col { + display: flex; + flex-direction: column; + } + + .cobalt-col .col-item { + } + ` + + @property({ attribute: false }) + plane?: DataPlane + + @property({ attribute: false }) + layoutChildren?: LayoutSpec[] + + @property({ attribute: false }) + parentContext: Record = {} + + override render() { + if ( !this.layoutChildren ) { + return html`...` + } + + return html` +
+ ${this.layoutChildren.map(x => this.renderItem(x))} +
+ ` + } + + private renderItem(spec: LayoutSpec) { + return html` +
+ +
+ ` + } +} diff --git a/src/app/resources/assets/app/cobalt/layout/components/Group.ts b/src/app/resources/assets/app/cobalt/layout/components/Group.ts new file mode 100644 index 0000000..22c539d --- /dev/null +++ b/src/app/resources/assets/app/cobalt/layout/components/Group.ts @@ -0,0 +1,52 @@ +import {css, html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {DataPlane} from '../../DataPlane' +import {LayoutSpec} from '../Layout' + +export type GroupConfig = { + display?: string + backgroundColor?: string + borderColor?: string +} + +@customElement('cobalt-group') +export class Group extends LitElement { + static override styles = css` + .group-wrapper { + padding: 7px; + } + + .group-wrapper .content { + border: 1px solid #999; + border-radius: 7px; + background: #cfcfcf; + } + ` + + @property({ attribute: false }) + config: GroupConfig = {} + + @property({ attribute: false }) + plane?: DataPlane + + @property({ attribute: false }) + layoutChildren?: LayoutSpec[] + + @property({ attribute: false }) + parentContext: Record = {} + + override render() { + return html` +
+
${this.config.display || ''}
+
+ +
+
+ ` + } +} diff --git a/src/app/resources/assets/app/cobalt/layout/components/Row.ts b/src/app/resources/assets/app/cobalt/layout/components/Row.ts new file mode 100644 index 0000000..6d34b36 --- /dev/null +++ b/src/app/resources/assets/app/cobalt/layout/components/Row.ts @@ -0,0 +1,51 @@ +import {css, html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {DataPlane} from '../../DataPlane' +import {LayoutSpec} from '../Layout' + +@customElement('cobalt-row') +export class Row extends LitElement { + static override styles = css` + .cobalt-row { + display: flex; + flex-direction: row; + } + + .cobalt-row .row-item { + flex: 1; + } + ` + + @property({ attribute: false }) + plane?: DataPlane + + @property({ attribute: false }) + layoutChildren?: LayoutSpec[] + + @property({ attribute: false }) + parentContext: Record = {} + + override render() { + if ( !this.layoutChildren ) { + return html`...` + } + + return html` +
+ ${this.layoutChildren.map(x => this.renderItem(x))} +
+ ` + } + + private renderItem(spec: LayoutSpec) { + return html` +
+ +
+ ` + } +} diff --git a/src/app/resources/assets/app/cobalt/layout/components/Title.ts b/src/app/resources/assets/app/cobalt/layout/components/Title.ts new file mode 100644 index 0000000..7eae4ed --- /dev/null +++ b/src/app/resources/assets/app/cobalt/layout/components/Title.ts @@ -0,0 +1,29 @@ +import {css, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {DataPlane} from '../../DataPlane' +import {LayoutSpec} from '../Layout' + +export type TitleConfig = { + display?: string +} + +@customElement('cobalt-title') +export class Title extends LitElement { + static override styles = css` + + ` + + @property({ attribute: false }) + config: TitleConfig = {} + + @property({ attribute: false }) + plane?: DataPlane + + @property({ attribute: false }) + layoutChildren?: LayoutSpec[] + + @property({ attribute: false }) + parentContext: Record = {} + + +} diff --git a/src/app/resources/assets/app/cobalt/layout/components/index.ts b/src/app/resources/assets/app/cobalt/layout/components/index.ts new file mode 100644 index 0000000..56eafbe --- /dev/null +++ b/src/app/resources/assets/app/cobalt/layout/components/index.ts @@ -0,0 +1,5 @@ + +export * from './Row' +export * from './Column' +export * from './Group' +export * from './Card' diff --git a/src/app/resources/assets/app/cobalt/list/Table.ts b/src/app/resources/assets/app/cobalt/list/Table.ts new file mode 100644 index 0000000..3dd4132 --- /dev/null +++ b/src/app/resources/assets/app/cobalt/list/Table.ts @@ -0,0 +1,123 @@ +import {css, html, LitElement} from 'lit' +import {customElement, property} from 'lit/decorators.js' +import {unsafeHTML} from 'lit/directives/unsafe-html.js' +import {DataPlane} from '../DataPlane' +import type {FieldDefinition, ListConfig} from '../types' +import {FieldType} from '../types' +import {IBehaviorSubject, Unsubscribe} from '../../BehaviorSubject' +import {Ex} from '../../util' +import dateToDisplay = Ex.dateToDisplay + +@customElement('cobalt-table') +export class Table extends LitElement { + static override styles = css` + table { + border-collapse: collapse; + width: 100%; + } + + td, th { + border: 1px solid #ccc; + text-align: left; + padding: 7px; + } + ` + + @property({ attribute: false }) + plane?: DataPlane + + @property({ attribute: false }) + config?: ListConfig + + @property({ attribute: false }) + parentContext: Record = {} + + private data: Record[] = [] + + private dataSub?: Unsubscribe + private rowField?: IBehaviorSubject + + override render() { + if ( !this.plane || !this.config ) { + return html`...` + } + + if ( !this.dataSub ) { + this.plane.ensureSource(this.config.sourceName) + + this.rowField = this.plane + .getField(this.config.sourceName, this.config.rowKey) + + if ( this.rowField ) { + this.dataSub = this.rowField + ?.subscribe(rows => { + this.updateData(rows) + this.requestUpdate() + }) + + this.updateData(this.rowField.value()) + } + } + + return html` + + + + ${this.config.fields.map(f => this.renderHeader(f))} + + ${this.data.map((r, i) => this.renderRow(i, r))} +
#
+ ` + } + + private updateData(rows: unknown) { + if ( !Array.isArray(rows) ) { + console.error('Could not update table data: rows not an array', rows) + return + } + + this.data = rows + } + + private renderHeader(field: FieldDefinition) { + return html` + ${field.display} + ` + } + + private renderRow(idx: number, row: Record) { + const cols = this.config!.fields + .map(field => { + if ( field.type === FieldType.html ) { + return html` + ${unsafeHTML(row[field.key] || '')} + ` + } + + if ( field.type === FieldType.date ) { + return html` + ${row[field.key] ? dateToDisplay(new Date(row[field.key])) : ''} + ` + } + + // fixme: handle select + + if ( field.type === FieldType.bool ) { + return html` + + ${row[field.key] ? 'Yes' : 'No'} + + ` + } + + return html` + ${row[field.key] || ''} + ` + }) + + return html` + ${idx+1} + ${cols} + ` + } +} diff --git a/src/app/resources/assets/app/cobalt/types.ts b/src/app/resources/assets/app/cobalt/types.ts new file mode 100644 index 0000000..2f66e93 --- /dev/null +++ b/src/app/resources/assets/app/cobalt/types.ts @@ -0,0 +1,134 @@ +export enum FieldType { + text = 'text', + textarea = 'textarea', + html = 'html', + email = 'email', + number = 'number', + integer = 'integer', + date = 'date', + select = 'select', + bool = 'bool', +} + +enum ResourceAction { + create = 'create', + read = 'read', + readOne = 'readOne', + update = 'update', + delete = 'delete', +} + +export enum Renderer { + text = 'text', + html = 'html', + bool = 'bool', + date = 'date', + time = 'time', + datetime = 'datetime', +} + +const allResourceActions = [ + ResourceAction.create, ResourceAction.read, ResourceAction.readOne, + ResourceAction.update, ResourceAction.delete, +] + +export type SourceRef = { + sourceName: string, + fieldName?: string, +} + +export type CobaltActionBase = { + successKey?: string, + resultKey?: string, + data?: Record, + gather?: Record, +} + +export type CobaltAction = CobaltActionBase & ( + { + type: 'route', + route: string, + method: string, + } +) + +export type LoadAction = { + target: SourceRef, + action: CobaltAction, +} + +export type CobaltActionResult = + { + success: true, + message?: string, + result?: unknown, + } + | { + success: false, + message?: string, + error?: unknown, + } + +export type OldCobaltAction = { + slug: string, + title: string, + color: string, + icon: string, + type: 'route', + route: string, + overall?: boolean, +} + +export type FieldBase = { + key: string, + display: string, + type: FieldType, + required: boolean, + sort?: 'asc' | 'desc', + renderer?: Renderer, + readonly?: boolean, + helpText?: string, + placeholder?: string, + queryable?: boolean, + hideOn?: { + form?: boolean, + listing?: boolean, + }, +} + +export type SelectOptions = {display: string, value: any, uuid?: string}[] + +export type FieldDefinition = FieldBase + | FieldBase & { type: FieldType.select, multiple?: boolean, options: SelectOptions } + +export type FormConfig = { + sourceName?: string, + fields: FieldDefinition[], +} + +export type ListConfig = { + sourceName: string, + rowKey: string, + fields: FieldDefinition[], +} + +/*export interface ResourceConfiguration { + key: string, + source: DataSource, + primaryKey: string, + orderField?: string, + orderDirection?: OrderDirection, + generateKeyOnInsert?: () => string|number, + processBeforeInsert?: (row: QueryRow) => Awaitable, + processAfterRead?: (row: QueryRow) => Awaitable, + display: { + field?: string, + singular: string, + plural: string, + }, + supportedActions: ResourceAction[], + otherActions: CobaltAction[], + fields: FieldDefinition[], +}*/ + +export { ResourceAction, allResourceActions } diff --git a/src/app/resources/assets/app/components/MessageContainer.ts b/src/app/resources/assets/app/components/MessageContainer.ts deleted file mode 100644 index a34b2c4..0000000 --- a/src/app/resources/assets/app/components/MessageContainer.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {Component} from '../Component' -import {MessageAlertEvent} from '../types' - -export class MessageContainer extends Component { - public activeAlerts: MessageAlertEvent[] = [] - - protected initialize() { - this.attachShadow({ mode: 'open' }) - - this.app() - ?.event('message.alert') - .subscribe(alert => { - this.activeAlerts.push(alert) - this.render() - }) - - this.render() - } - - render() { - if ( !this.shadowRoot ) { - return - } - - this.shadowRoot.innerHTML = ` -
- ${this.activeAlerts.map(x => '
' + x + '
')} -
- `; - } -} diff --git a/src/app/resources/assets/app/components/index.ts b/src/app/resources/assets/app/components/index.ts deleted file mode 100644 index 7100246..0000000 --- a/src/app/resources/assets/app/components/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import {MessageContainer} from './MessageContainer' - -export const registerComponents = () => { - customElements.define('ex-messages', MessageContainer) -} diff --git a/src/app/resources/assets/app/index.ts b/src/app/resources/assets/app/index.ts index 7d91438..61eae99 100644 --- a/src/app/resources/assets/app/index.ts +++ b/src/app/resources/assets/app/index.ts @@ -1,4 +1,8 @@ +import {app} from './instance' export * from './App' export * from './types' export * from './util' +export * from './cobalt' + +app() diff --git a/src/app/resources/assets/app/instance.ts b/src/app/resources/assets/app/instance.ts new file mode 100644 index 0000000..69aafc2 --- /dev/null +++ b/src/app/resources/assets/app/instance.ts @@ -0,0 +1,21 @@ +import {App} from './App' +import {BaseAppDataMap, BaseEventMap} from './types' + +type MyEventMap = BaseEventMap & { + 'wa.ready': {}, +} + +type MyAppDataMap = BaseAppDataMap & { + waReady: boolean, +} + +let appInstance: any = undefined +export const app = (): App => { + if ( !appInstance ) { + appInstance = new App() + appInstance.event('wa.ready') + .subscribe(() => appInstance.set('waReady', true)) + } + + return appInstance +} diff --git a/src/app/resources/assets/app/util.ts b/src/app/resources/assets/app/util.ts index ae570ed..7a0b9eb 100644 --- a/src/app/resources/assets/app/util.ts +++ b/src/app/resources/assets/app/util.ts @@ -24,7 +24,7 @@ export namespace Ex { export const uuid = (): string => { // @ts-ignore return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => - (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) + (c ^ crypto.getRandomValues(new Uint8Array(1))[0]! & 15 >> c / 4).toString(16) ) } diff --git a/src/app/resources/assets/dash2/main.css b/src/app/resources/assets/dash2/main.css new file mode 100644 index 0000000..7c91853 --- /dev/null +++ b/src/app/resources/assets/dash2/main.css @@ -0,0 +1,104 @@ +@font-face { + font-family: 'Red Hat Display'; + font-style: italic; + font-weight: 300 900; + font-display: swap; + src: url(/assets/font/redhat/RedHatDisplay-Italic-VariableFont_wght.ttf) format('woff2'); +} +@font-face { + font-family: 'Red Hat Display'; + font-style: normal; + font-weight: 300 900; + font-display: swap; + src: url(/assets/font/redhat/RedHatDisplay-VariableFont_wght.ttf) format('woff2'); +} + + +body { + background: #e0e0e0; + margin: 0; + padding: 0; + overflow: hidden; + font-family: "Red Hat Display", sans-serif; +} + +.wrapper { + display: flex; + flex-direction: row; + height: 100vh; + overflow: hidden; +} + +nav { + min-width: 300px; + margin: 30px; + margin-right: 15px; +} + +nav ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +nav li { + border-radius: 20px; + background: #e0e0e0; + box-shadow: 4px 4px 9px #bebebe, -4px -4px 9px #ffffff; + padding: 7px 20px; + margin: 25px 0; + transition: all 0.2s linear; + font-size: 1.15em; + font-weight: 475; +} + +nav li:hover { + box-shadow: 4px 4px 9px #cecece, -4px -4px 9px #efefef; + cursor: pointer; +} + +.wrapper wa-divider { + margin-right: 0; +} + +.layout { + flex: 1; + display: flex; + flex-direction: column; + overflow: scroll; + padding-bottom: 10vh; +} + +header { + display: flex; + flex-direction: row; + align-items: center; + padding: 10px 15px 0; +} + +header h1 { + margin: 0; +} + +header .left { + flex: 1; +} + +.main-content { + flex: 1; + margin: 0 15px; +} + + + + + + + +wa-divider { + --color: #aaa; +} + +wa-dropdown *[slot="trigger"]:hover { + cursor: pointer; +} diff --git a/src/app/resources/assets/dash2/main.js b/src/app/resources/assets/dash2/main.js new file mode 100644 index 0000000..31fcec7 --- /dev/null +++ b/src/app/resources/assets/dash2/main.js @@ -0,0 +1,17 @@ +(() => { + + const userMenu = document.querySelector('wa-menu#user-menu') + userMenu.addEventListener('wa-select', event => { + console.log('user menu click', event) + + const item = event.detail.item.value + if ( item === 'old' ) { + window.location.href = '/dash' + } else if ( item === 'home' ) { + window.location.href = '/' + } else if ( item === 'logout' ) { + window.location.href = '/auth/coreid/logout' + } + }) + +})(); diff --git a/src/app/resources/assets/font/redhat/OFL.txt b/src/app/resources/assets/font/redhat/OFL.txt new file mode 100644 index 0000000..1cef9c3 --- /dev/null +++ b/src/app/resources/assets/font/redhat/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2021 The Red Hat Project Authors (https://github.com/RedHatOfficial/RedHatFont) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/app/resources/assets/font/redhat/README.txt b/src/app/resources/assets/font/redhat/README.txt new file mode 100644 index 0000000..4c3d872 --- /dev/null +++ b/src/app/resources/assets/font/redhat/README.txt @@ -0,0 +1,77 @@ +Red Hat Display Variable Font +============================= + +This download contains Red Hat Display as both variable fonts and static fonts. + +Red Hat Display is a variable font with this axis: + wght + +This means all the styles are contained in these files: + RedHatDisplay-VariableFont_wght.ttf + RedHatDisplay-Italic-VariableFont_wght.ttf + +If your app fully supports variable fonts, you can now pick intermediate styles +that aren’t available as static fonts. Not all apps support variable fonts, and +in those cases you can use the static font files for Red Hat Display: + static/RedHatDisplay-Light.ttf + static/RedHatDisplay-Regular.ttf + static/RedHatDisplay-Medium.ttf + static/RedHatDisplay-SemiBold.ttf + static/RedHatDisplay-Bold.ttf + static/RedHatDisplay-ExtraBold.ttf + static/RedHatDisplay-Black.ttf + static/RedHatDisplay-LightItalic.ttf + static/RedHatDisplay-Italic.ttf + static/RedHatDisplay-MediumItalic.ttf + static/RedHatDisplay-SemiBoldItalic.ttf + static/RedHatDisplay-BoldItalic.ttf + static/RedHatDisplay-ExtraBoldItalic.ttf + static/RedHatDisplay-BlackItalic.ttf + +Get started +----------- + +1. Install the font files you want to use + +2. Use your app's font picker to view the font family and all the +available styles + +Learn more about variable fonts +------------------------------- + + https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts + https://variablefonts.typenetwork.com + https://medium.com/variable-fonts + +In desktop apps + + https://theblog.adobe.com/can-variable-fonts-illustrator-cc + https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts + +Online + + https://developers.google.com/fonts/docs/getting_started + https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide + https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts + +Installing fonts + + MacOS: https://support.apple.com/en-us/HT201749 + Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux + Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows + +Android Apps + + https://developers.google.com/fonts/docs/android + https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts + +License +------- +Please read the full license text (OFL.txt) to understand the permissions, +restrictions and requirements for usage, redistribution, and modification. + +You can use them in your products & projects – print or digital, +commercial or otherwise. + +This isn't legal advice, please consider consulting a lawyer and see the full +license for all details. diff --git a/src/app/resources/assets/font/redhat/RedHatDisplay-Italic-VariableFont_wght.ttf b/src/app/resources/assets/font/redhat/RedHatDisplay-Italic-VariableFont_wght.ttf new file mode 100644 index 0000000..2215db7 Binary files /dev/null and b/src/app/resources/assets/font/redhat/RedHatDisplay-Italic-VariableFont_wght.ttf differ diff --git a/src/app/resources/assets/font/redhat/RedHatDisplay-VariableFont_wght.ttf b/src/app/resources/assets/font/redhat/RedHatDisplay-VariableFont_wght.ttf new file mode 100644 index 0000000..84abb78 Binary files /dev/null and b/src/app/resources/assets/font/redhat/RedHatDisplay-VariableFont_wght.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-Black.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Black.ttf new file mode 100644 index 0000000..c2e6c8a Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Black.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-BlackItalic.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-BlackItalic.ttf new file mode 100644 index 0000000..9cd87c0 Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-BlackItalic.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-Bold.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Bold.ttf new file mode 100644 index 0000000..c952056 Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Bold.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-BoldItalic.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-BoldItalic.ttf new file mode 100644 index 0000000..428c430 Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-BoldItalic.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-ExtraBold.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-ExtraBold.ttf new file mode 100644 index 0000000..53fa55a Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-ExtraBold.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-ExtraBoldItalic.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-ExtraBoldItalic.ttf new file mode 100644 index 0000000..159d684 Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-ExtraBoldItalic.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-Italic.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Italic.ttf new file mode 100644 index 0000000..353c11c Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Italic.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-Light.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Light.ttf new file mode 100644 index 0000000..fe33d8a Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Light.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-LightItalic.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-LightItalic.ttf new file mode 100644 index 0000000..8c08645 Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-LightItalic.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-Medium.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Medium.ttf new file mode 100644 index 0000000..032e44d Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Medium.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-MediumItalic.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-MediumItalic.ttf new file mode 100644 index 0000000..50e6129 Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-MediumItalic.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-Regular.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Regular.ttf new file mode 100644 index 0000000..a1cee81 Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-Regular.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-SemiBold.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-SemiBold.ttf new file mode 100644 index 0000000..da13756 Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-SemiBold.ttf differ diff --git a/src/app/resources/assets/font/redhat/static/RedHatDisplay-SemiBoldItalic.ttf b/src/app/resources/assets/font/redhat/static/RedHatDisplay-SemiBoldItalic.ttf new file mode 100644 index 0000000..e50d881 Binary files /dev/null and b/src/app/resources/assets/font/redhat/static/RedHatDisplay-SemiBoldItalic.ttf differ diff --git a/src/app/resources/views/dash2/cobalt.pug b/src/app/resources/views/dash2/cobalt.pug new file mode 100644 index 0000000..29c5a24 --- /dev/null +++ b/src/app/resources/views/dash2/cobalt.pug @@ -0,0 +1,14 @@ +extends index + +block content + div#preloader(style="display: none") + wa-input + wa-textarea + wa-switch + wa-select + wa-option + cobalt-host( + settingsendpoint='/dash2/cobalt/settings' + layoutname=layout, + initialparamsjson=JSON.stringify(params) + ) diff --git a/src/app/resources/views/dash2/home.pug b/src/app/resources/views/dash2/home.pug new file mode 100644 index 0000000..8faccb5 --- /dev/null +++ b/src/app/resources/views/dash2/home.pug @@ -0,0 +1,4 @@ +extends index + +block content + p Welcome! diff --git a/src/app/resources/views/dash2/index.pug b/src/app/resources/views/dash2/index.pug new file mode 100644 index 0000000..604ee32 --- /dev/null +++ b/src/app/resources/views/dash2/index.pug @@ -0,0 +1,59 @@ +doctype html +html + head + title #{title ? title + ' | ' : ''}Dashboard + + meta(charset='utf-8') + meta(name='viewport' content='width=device-width, initial-scale=1') + + block styles + link(rel='stylesheet' href='https://early.webawesome.com/webawesome@3.0.0-alpha.2/dist/themes/default.css') + link(rel='stylesheet' href=asset('dash2/main.css')) + + body + .wrapper + nav + ul + li Home + li Another Page + li Yet Another Page + wa-divider + each resource in resources + li(onclick=`window.location.href="${resource.href}"`) #{resource.display} + wa-divider(vertical) + div.layout + header + div.left + if title + h1 #{title} + div.right + wa-dropdown + wa-avatar(slot='trigger' initials=appData.user.initials image=appData.user.photoUrl) + wa-menu#user-menu + wa-menu-label Hi, #{appData.user.firstName}. + wa-menu-item(value='home') + wa-icon(slot='prefix' name='house' variant='solid') + | Go to Homepage + wa-menu-item(value='old') + wa-icon(slot='prefix' name='repeat' variant='solid') + | Switch to Old Dashboard + wa-divider + wa-menu-item(value='logout') + wa-icon(slot='prefix' name='right-from-bracket' variant='solid') + | Logout + + wa-divider + div.main-content + block content + + block scripts + script. + window.appData = !{JSON.stringify(appData)} + + script(src=asset('app.js')) + script(type='module' src='https://early.webawesome.com/webawesome@3.0.0-alpha.2/dist/webawesome.loader.js') + script(type='module'). + setTimeout(() => window.exAppInstance.fire('wa.ready', {}), 2000) + script(src=asset('dash2/main.js')) + + diff --git a/tsconfig.client.json b/tsconfig.client.json index 84f4bbc..6848f4b 100644 --- a/tsconfig.client.json +++ b/tsconfig.client.json @@ -1,9 +1,29 @@ { - "extends": "./tsconfig.json", "compilerOptions": { - "module": "esnext", + "declaration": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "preserveSymlinks": true, "outDir": "lib/app/resources/assets/app", - "lib": ["dom", "es2015"] + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "strict": true, + "allowSyntheticDefaultImports": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "module": "esnext", + "noEmit": true, + "lib": [ + "es2022", + "dom", + "dom.iterable" + ] }, "include": ["src/app/resources/assets/app"] } diff --git a/tsconfig.json b/tsconfig.json index 0f80131..34b6309 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "esnext", + "target": "es2022", "module": "commonjs", "declaration": true, "outDir": "./lib", diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..b734391 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,22 @@ +const path = require('path') + +module.exports = { + mode: 'production', + entry: './lib/app/resources/assets/app/index.js', + output: { + filename: 'app.js', + path: path.resolve(__dirname, 'lib', 'app', 'resources', 'assets'), + }, + module: { + rules: [ + { + test: /\.css$/, + use: ['style-loader', 'css-loader'], + }, + { + test: /\.ttf$/, + type: 'asset/resource' + }, + ], + }, +}