diff --git a/.gitignore b/.gitignore index 13e00578..c5ff3199 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,9 @@ newman config.json uploads/ -.docker/mitmproxy-data \ No newline at end of file +.docker/mitmproxy-data +.DS_Store + +# auto generated files +apps/juxtaposition-ui/webfiles/ctr/css/sprites.css +apps/juxtaposition-ui/webfiles/ctr/images/sprites.png \ No newline at end of file diff --git a/apps/juxtaposition-ui/eslint.config.mjs b/apps/juxtaposition-ui/eslint.config.mjs index ebf67b40..6e2148b4 100644 --- a/apps/juxtaposition-ui/eslint.config.mjs +++ b/apps/juxtaposition-ui/eslint.config.mjs @@ -15,11 +15,16 @@ export default defineConfig([ rules: { 'no-restricted-imports': 'off' // It's a special compile step, we can't use path aliases }, - files: ['src/webfiles/**/*.js'] + files: ['webfiles/**/*.js', 'webfiles/**/*.ts'] }, { // Rules that apply to the 3DS (CTR) and Wii U (Portal) browsers - files: ['src/webfiles/ctr/**/*.js', 'src/webfiles/portal/**/*.js'], + files: [ + 'webfiles/ctr/**/*.js', + 'webfiles/ctr/**/*.ts', + 'webfiles/portal/**/*.js', + 'webfiles/portal/**/*.ts' + ], rules: { 'no-var': 'off' // 3DS and Wii U browsers need to use var }, @@ -46,6 +51,6 @@ export default defineConfig([ ...globals.builtin } }, - ignores: ['src/webfiles/**/*.js'] + ignores: ['webfiles/**/*.js', 'webfiles/**/*.ts'] } ]); diff --git a/apps/juxtaposition-ui/nodemon.json b/apps/juxtaposition-ui/nodemon.json index cabe691d..c97819e9 100644 --- a/apps/juxtaposition-ui/nodemon.json +++ b/apps/juxtaposition-ui/nodemon.json @@ -1,8 +1,13 @@ { "watch": [ - "src/" + "src/", + "webfiles/" ], "ext": "js,ts,ejs,tsx,jsx,json,css", + "ignore": [ + "webfiles/ctr/css/sprites.css", + "webfiles/ctr/images/sprites.png" + ], "env": { "NODE_ENV": "development" } diff --git a/apps/juxtaposition-ui/package.json b/apps/juxtaposition-ui/package.json index 4fd4f9d7..4d1c7f88 100644 --- a/apps/juxtaposition-ui/package.json +++ b/apps/juxtaposition-ui/package.json @@ -13,31 +13,31 @@ "lint": "eslint .", "lint:fix": "eslint . --fix", "build": "tsup && tsc --noEmit", - "start": "node --enable-source-maps dist/server.js" + "start": "node --enable-source-maps dist/server.js", + "build:sprites": "node scripts/sprites.ts" }, "dependencies": { - "@aws-sdk/client-s3": "^3.958.0", - "@imagemagick/magick-wasm": "^0.0.37", + "@aws-sdk/client-s3": "^3.980.0", + "@imagemagick/magick-wasm": "^0.0.38", "@neato/config": "^4.1.0", - "@pretendonetwork/grpc": "^2.3.5", + "@pretendonetwork/grpc": "^2.4.1", "@repo/grpc-client": "^0.0.0", "classnames": "^2.5.1", - "colors": "^1.4.0", "connect-redis": "^9.0.0", "cookie-parser": "^1.4.7", "crc": "^4.3.2", - "ejs": "^3.1.10", - "esbuild-plugin-copy": "^2.1.1", - "express": "^4.21.2", + "ejs": "^4.0.1", + "express": "^4.22.1", "express-async-errors": "^3.1.1", "express-prom-bundle": "^7.0.2", - "express-rate-limit": "^8.1.0", - "express-session": "^1.18.2", + "express-rate-limit": "^8.2.1", + "express-session": "^1.19.0", "express-subdomain": "^1.0.6", "hashmap": "^2.4.0", "luxon": "^3.7.2", + "method-override": "^3.0.0", "moment": "^2.30.1", - "mongoose": "^8.19.2", + "mongoose": "^8.22.0", "mongoose-fuzzy-search-next": "^1.0.13", "multer": "^2.0.2", "nice-grpc": "^2.1.14", @@ -45,20 +45,28 @@ "pako": "^2.1.0", "pino": "^10.1.0", "pino-http": "^11.0.0", - "pino-pretty": "^13.1.2", + "pino-pretty": "^13.1.3", "pjax": "^0.2.8", "prom-client": "^15.1.3", "react": "^19.1.1", - "react-dom": "^19.2.0", + "react-dom": "^19.2.4", "redis": "^5.10.0", - "tsx": "^4.20.6", - "zod": "^4.1.13" + "tsx": "^4.21.0", + "zod": "^4.3.6" }, "devDependencies": { + "@pretendonetwork/cave-types": "^1.0.2", "@pretendonetwork/eslint-config": "^0.1.3", + "@pretendonetwork/wiiu-browser-types": "^1.0.0", + "@pretendonetwork/wiiu-dialog-types": "^1.0.0", + "@pretendonetwork/wiiu-error-viewer-types": "^1.0.0", + "@repo/esbuild-plugin-oxipng": "^0.0.0", + "@repo/esbuild-plugin-spritesmith": "^0.0.0", + "@types/cookie-parser": "^1.4.10", "@types/express-session": "^1.18.2", "@types/hashmap": "^2.3.4", "@types/luxon": "^3.7.1", + "@types/method-override": "^3.0.0", "@types/node": "^22.19.1", "@types/react": "^19.1.13", "@types/react-dom": "^19.2.3", @@ -67,7 +75,8 @@ "ejs-lint": "^2.0.1", "esbuild-fix-imports-plugin": "^1.0.23", "esbuild-raw-plugin": "^0.3.1", - "eslint": "^9.39.1", + "esbuild-plugin-copy": "^2.1.1", + "eslint": "^9.39.2", "globals": "^16.5.0", "nodemon": "^3.1.11", "npm-run-all": "^4.1.5", diff --git a/apps/juxtaposition-ui/src/api/post.ts b/apps/juxtaposition-ui/src/api/post.ts index a3ce595b..3b7c54eb 100644 --- a/apps/juxtaposition-ui/src/api/post.ts +++ b/apps/juxtaposition-ui/src/api/post.ts @@ -89,11 +89,12 @@ export async function getPostsByEmpathy(tokens: UserTokens, empathy_by: number, return posts; } -export async function getPostsByParentId(tokens: UserTokens, parent_id: string, offset: number): Promise | null> { +export async function getPostsByParentId(tokens: UserTokens, parent_id: string, offset: number, limit: number): Promise | null> { const posts = await apiFetchUser>(tokens, `/api/v1/posts`, { query: { parent_id: parent_id, offset, + limit, include_replies: true, sort: 'oldest' } diff --git a/apps/juxtaposition-ui/src/config.ts b/apps/juxtaposition-ui/src/config.ts index b086317c..e51aafcc 100644 --- a/apps/juxtaposition-ui/src/config.ts +++ b/apps/juxtaposition-ui/src/config.ts @@ -34,6 +34,8 @@ const schema = z.object({ whitelist: z.string(), /** Environment (prod/test/dev) to use for Discovery and access_level control. */ serverEnvironment: z.string(), + /** By default, only consoles can reach the console domains. This setting disable that constraint */ + disableConsoleChecks: z.stringbool().default(false), /** The AES key to use for decrypting service tokens. Must match the account server's. */ aesKey: z.string(), mongoose: z.object({ @@ -78,6 +80,7 @@ export const presets = { cdnDomain: 'https://cdn.pretendo.cc/miiverse', whitelist: '', serverEnvironment: 'prod', + disableConsoleChecks: true, aesKey: '1234567812345678123456781234567812345678123456781234567812345678', mongoose: { uri: 'mongodb://localhost:27017/miiverse?directConnection=true' diff --git a/apps/juxtaposition-ui/src/database.js b/apps/juxtaposition-ui/src/database.js index a9d522d0..fc2eddf5 100644 --- a/apps/juxtaposition-ui/src/database.js +++ b/apps/juxtaposition-ui/src/database.js @@ -34,6 +34,10 @@ function verifyConnected() { } } +export function notBanned() { + return { account_status: { $in: [0, 1] } }; +} + async function getCommunities(numberOfCommunities, offset) { verifyConnected(); if (!offset) { @@ -288,7 +292,8 @@ async function getUserContent(pid) { async function getFollowingUsers(content) { verifyConnected(); return SETTINGS.find({ - pid: content.following_users + pid: content.following_users, + ...notBanned() }); } diff --git a/apps/juxtaposition-ui/src/metrics.ts b/apps/juxtaposition-ui/src/metrics.ts index e32ac302..c88dc9b2 100644 --- a/apps/juxtaposition-ui/src/metrics.ts +++ b/apps/juxtaposition-ui/src/metrics.ts @@ -4,7 +4,7 @@ import express from 'express'; import { logger } from '@/logger'; import { config } from '@/config'; import { SETTINGS } from '@/models/settings'; -import type { Express } from 'express'; +import type { Express, NextFunction, Request, Response } from 'express'; export const onlineNowGauge = new Gauge({ name: 'juxtaposition_online_users_now', @@ -12,11 +12,11 @@ export const onlineNowGauge = new Gauge({ async collect(): Promise { const onlineRangeMs = 10 * 60 * 1000; // 10 minutes const cutoff = new Date(Date.now() - onlineRangeMs); - const [{ n }] = await SETTINGS.aggregate<{ n: number }>([ + const [result] = await SETTINGS.aggregate<{ n: number } | undefined>([ { $match: { last_active: { $gt: cutoff } } }, { $count: 'n' } ]); - this.set(n); + this.set(result?.n ?? 0); } }); @@ -26,11 +26,11 @@ export const activeMonthlyGauge = new Gauge({ async collect(): Promise { const monthlyRangeMs = 30 * 24 * 60 * 60 * 1000; const cutoff = new Date(Date.now() - monthlyRangeMs); - const [{ n }] = await SETTINGS.aggregate<{ n: number }>([ + const [result] = await SETTINGS.aggregate<{ n: number } | undefined>([ { $match: { last_active: { $gt: cutoff } } }, { $count: 'n' } ]); - this.set(n); + this.set(result?.n ?? 0); } }); @@ -40,11 +40,11 @@ export const activeYearlyGauge = new Gauge({ async collect(): Promise { const yearlyRangeMs = 365 * 24 * 60 * 60 * 1000; const cutoff = new Date(Date.now() - yearlyRangeMs); - const [{ n }] = await SETTINGS.aggregate<{ n: number }>([ + const [result] = await SETTINGS.aggregate<{ n: number } | undefined>([ { $match: { last_active: { $gt: cutoff } } }, { $count: 'n' } ]); - this.set(n); + this.set(result?.n ?? 0); } }); @@ -87,6 +87,11 @@ export function registerMetrics(app: Express): Express { })); } + metrics.use((error: Error, req: Request, res: Response, _next: NextFunction) => { + logger.error(error, 'Request failed!'); + res.sendStatus(500); + }); + return metrics; } diff --git a/apps/juxtaposition-ui/src/middleware/checkBan.ts b/apps/juxtaposition-ui/src/middleware/checkBan.tsx similarity index 75% rename from apps/juxtaposition-ui/src/middleware/checkBan.ts rename to apps/juxtaposition-ui/src/middleware/checkBan.tsx index 1f329e11..2d72dd30 100644 --- a/apps/juxtaposition-ui/src/middleware/checkBan.ts +++ b/apps/juxtaposition-ui/src/middleware/checkBan.tsx @@ -2,6 +2,10 @@ import moment from 'moment'; import { database as db } from '@/database'; import { config } from '@/config'; import { humanDate, humanFromNow } from '@/util'; +import { WebLoginView } from '@/services/juxt-web/views/web/loginView'; +import { buildContext } from '@/services/juxt-web/views/context'; +import { CtrFatalErrorView } from '@/services/juxt-web/views/ctr/errorView'; +import { PortalFatalErrorView } from '@/services/juxt-web/views/portal/errorView'; import type { RequestHandler } from 'express'; export const checkBan: RequestHandler = async (request, response, next) => { @@ -41,14 +45,13 @@ export const checkBan: RequestHandler = async (request, response, next) => { if (!accessAllowed) { response.status(500); - if (request.directory === 'web') { - return response.render('web/login.ejs', { toast: 'No access. Must be tester or dev', redirect: request.originalUrl }); - } else { - return response.render('portal/error_fatal.ejs', { - code: 5989999, - message: 'No access. Must be tester or dev' - }); - } + const banMessage = 'No access. Must be tester or dev'; + const banCode = 5989999; + return response.jsxForDirectory({ + web: , + portal: , + ctr: + }); } const userSettings = await db.getUserSettings(request.pid); if (userSettings && moment(userSettings.ban_lift_date) <= moment() && userSettings.account_status !== 3) { @@ -79,17 +82,11 @@ export const checkBan: RequestHandler = async (request, response, next) => { } banMessage += `\n\nIf you have any questions, please contact the moderators on the Pretendo Network Forum (https://preten.do/ban-appeal/).`; - if (request.directory === 'web') { - return response.render('web/login.ejs', { - toast: banMessage, - redirect: request.originalUrl - }); - } else { - return response.render(request.directory + '/error_fatal.ejs', { - message: banMessage, - code: banCode - }); - } + return response.jsxForDirectory({ + web: , + portal: , + ctr: + }); } if (userSettings) { diff --git a/apps/juxtaposition-ui/src/middleware/consoleAuth.ts b/apps/juxtaposition-ui/src/middleware/consoleAuth.tsx similarity index 65% rename from apps/juxtaposition-ui/src/middleware/consoleAuth.ts rename to apps/juxtaposition-ui/src/middleware/consoleAuth.tsx index 0aed82e0..e3326661 100644 --- a/apps/juxtaposition-ui/src/middleware/consoleAuth.ts +++ b/apps/juxtaposition-ui/src/middleware/consoleAuth.tsx @@ -1,7 +1,16 @@ import { config } from '@/config'; import { logger } from '@/logger'; +import { CtrFatalErrorView } from '@/services/juxt-web/views/ctr/errorView'; +import { PortalFatalErrorView } from '@/services/juxt-web/views/portal/errorView'; import { decodeParamPack, getPIDFromServiceToken, getUserAccountData, getUserDataFromToken, processLanguage } from '@/util'; -import type { RequestHandler } from 'express'; +import type { RequestHandler, Response } from 'express'; + +function renderAuthError(res: Response, code: number, message: string): void { + res.jsxForDirectory({ + portal: , + ctr: + }); +} export const consoleAuth: RequestHandler = async (request, response, next) => { // Get pid and fetch user data @@ -24,11 +33,11 @@ export const consoleAuth: RequestHandler = async (request, response, next) => { request.paramPackData = ppack ? decodeParamPack(ppack) : null; response.header('X-Nintendo-WhiteList', config.whitelist); - if (!request.user) { + if (!request.user && request.cookies.access_token) { try { - // Developer accounts may also use an OAuth token for console frontends + // Developer accounts may bypass console checks const user = await getUserDataFromToken(request.cookies.access_token); - if (user.accessLevel === 3) { + if (user.accessLevel === 3 || config.disableConsoleChecks) { request.user = await getUserAccountData(user.pid); request.pid = user.pid; @@ -50,32 +59,23 @@ export const consoleAuth: RequestHandler = async (request, response, next) => { } } + const mayBypassAuthChecks = request.user?.accessLevel === 3 || config.disableConsoleChecks; + // This section includes checks if a user is a developer and adds exceptions for these cases if (!request.pid) { - return response.render('portal/error_fatal.ejs', { - code: 5989999, - message: 'Unable to parse service token. Are you using a Nintendo Network ID?' - }); + return renderAuthError(response, 5989999, 'Unable to parse service token. Are you using a Nintendo Network ID?'); } if (!request.user) { - return response.render('portal/error_fatal.ejs', { - code: 5989999, - message: 'Unable to fetch user data. Please try again later.' - }); + return renderAuthError(response, 5989999, 'Unable to fetch user data. Please try again later.'); } - if (request.user.accessLevel < 3 && !request.paramPackData) { - return response.render('portal/error_fatal.ejs', { - code: 5989999, - message: 'Missing auth headers' - }); + + if (!mayBypassAuthChecks && !request.paramPackData) { + return renderAuthError(response, 5989999, 'Missing auth headers'); } const userAgent = request.get('user-agent') ?? ''; const uaIsConsole = userAgent.includes('Nintendo WiiU') || userAgent.includes('Nintendo 3DS'); - if (request.user.accessLevel < 3 && (request.cookies.access_token || !uaIsConsole)) { - return response.render('portal/error_fatal.ejs', { - code: 5989999, - message: 'Invalid authentication method used.' - }); + if (!mayBypassAuthChecks && !uaIsConsole) { + return renderAuthError(response, 5989999, 'Invalid authentication method used.'); } response.locals.uaIsConsole = uaIsConsole; diff --git a/apps/juxtaposition-ui/src/middleware/detectVersion.js b/apps/juxtaposition-ui/src/middleware/detectVersion.ts similarity index 70% rename from apps/juxtaposition-ui/src/middleware/detectVersion.js rename to apps/juxtaposition-ui/src/middleware/detectVersion.ts index 5219a352..7dbfb139 100644 --- a/apps/juxtaposition-ui/src/middleware/detectVersion.js +++ b/apps/juxtaposition-ui/src/middleware/detectVersion.ts @@ -1,7 +1,7 @@ import { processLanguage } from '@/util'; +import type { Request, RequestHandler } from 'express'; -export async function detectVersion(request, response, next) { - request.timerDate = Date.now(); +export const detectVersion: RequestHandler = async (request, response, next) => { // Check the domain and set the directory if (includes(request, 'juxt')) { request.directory = 'web'; @@ -13,8 +13,8 @@ export async function detectVersion(request, response, next) { request.isWrite = request.method === 'POST' || request.method === 'PUT' || request.method === 'DELETE'; next(); -} +}; -function includes(request, domain) { +function includes(request: Request, domain: string): boolean { return request.subdomains.findIndex(element => element.includes(domain)) !== -1; } diff --git a/apps/juxtaposition-ui/src/middleware/discovery.js b/apps/juxtaposition-ui/src/middleware/discovery.tsx similarity index 53% rename from apps/juxtaposition-ui/src/middleware/discovery.js rename to apps/juxtaposition-ui/src/middleware/discovery.tsx index b4bbacc3..9919b5c6 100644 --- a/apps/juxtaposition-ui/src/middleware/discovery.js +++ b/apps/juxtaposition-ui/src/middleware/discovery.tsx @@ -1,7 +1,12 @@ import { database as db } from '@/database'; import { config } from '@/config'; +import { WebLoginView } from '@/services/juxt-web/views/web/loginView'; +import { buildContext } from '@/services/juxt-web/views/context'; +import { PortalFatalErrorView } from '@/services/juxt-web/views/portal/errorView'; +import { CtrFatalErrorView } from '@/services/juxt-web/views/ctr/errorView'; +import type { RequestHandler } from 'express'; -export async function checkDiscovery(request, response, next) { +export const checkDiscovery: RequestHandler = async (request, response, next) => { const discovery = await db.getEndPoint(config.serverEnvironment); if (!discovery || discovery.status !== 0) { @@ -18,14 +23,11 @@ export async function checkDiscovery(request, response, next) { message = 'Juxtaposition is currently unavailable. Please try again later.'; break; } - if (request.directory === 'web') { - return response.render('web/login.ejs', { toast: message, redirect: request.originalUrl }); - } else { - return response.render('portal/error_fatal.ejs', { - message, - code: 5989999 - }); - } + return response.jsxForDirectory({ + web: , + portal: , + ctr: + }); } else { request.guest_access = discovery ? discovery.guest_access : false; request.new_users = discovery ? discovery.new_users : false; @@ -33,4 +35,4 @@ export async function checkDiscovery(request, response, next) { } next(); -} +}; diff --git a/apps/juxtaposition-ui/src/middleware/jsx.ts b/apps/juxtaposition-ui/src/middleware/jsx.tsx similarity index 61% rename from apps/juxtaposition-ui/src/middleware/jsx.ts rename to apps/juxtaposition-ui/src/middleware/jsx.tsx index bc135669..a8470b15 100644 --- a/apps/juxtaposition-ui/src/middleware/jsx.ts +++ b/apps/juxtaposition-ui/src/middleware/jsx.tsx @@ -1,6 +1,11 @@ import { renderToStaticMarkup } from 'react-dom/server'; -import type { RequestHandler } from 'express'; +import { buildContext } from '@/services/juxt-web/views/context'; +import { WebErrorView } from '@/services/juxt-web/views/web/errorView'; +import { CtrErrorView } from '@/services/juxt-web/views/ctr/errorView'; +import { PortalErrorView } from '@/services/juxt-web/views/portal/errorView'; import type { ReactElement } from 'react'; +import type { RequestHandler } from 'express'; +import type { ErrorViewProps } from '@/services/juxt-web/views/web/errorView'; const htmlDoctype = ''; @@ -15,7 +20,7 @@ export function renderJsx(el: ReactElement): string { */ export const jsxRenderer: RequestHandler = (request, response, next) => { response.jsx = (el, addDoctype): typeof response => { - const prefix = addDoctype ? htmlDoctype + '\n' : ''; + const prefix = (addDoctype ?? true) ? htmlDoctype + '\n' : ''; response.send(prefix + renderJsx(el)); return response; }; @@ -40,5 +45,20 @@ export const jsxRenderer: RequestHandler = (request, response, next) => { throw new Error('Invalid directory to render JSX for'); }; + + response.renderError = (opt): typeof response => { + const props: ErrorViewProps = { + ctx: buildContext(response), + requestId: request.id, + code: opt.code, + message: opt.message + }; + response.jsxForDirectory({ + web: , + ctr: , + portal: + }); + return response; + }; next(); }; diff --git a/apps/juxtaposition-ui/src/middleware/webAuth.ts b/apps/juxtaposition-ui/src/middleware/webAuth.ts index 0e0dcb83..f41ebb73 100644 --- a/apps/juxtaposition-ui/src/middleware/webAuth.ts +++ b/apps/juxtaposition-ui/src/middleware/webAuth.ts @@ -11,7 +11,7 @@ export const webAuth: RequestHandler = async (request, response, next) => { if (request.session && request.session.user && request.session.pid && !request.isWrite && request.cookies.access_token) { request.user = request.session.user; request.pid = request.session.pid; - } else { + } else if (request.cookies.access_token) { try { request.pid = (await getUserDataFromToken(request.cookies.access_token)).pid; request.user = await getUserAccountData(request.pid); diff --git a/apps/juxtaposition-ui/src/models/endpoint.js b/apps/juxtaposition-ui/src/models/endpoint.js deleted file mode 100644 index 6512223c..00000000 --- a/apps/juxtaposition-ui/src/models/endpoint.js +++ /dev/null @@ -1,18 +0,0 @@ -import { Schema, model } from 'mongoose'; - -export const endpointSchema = new Schema({ - status: Number, - server_access_level: String, - topics: Boolean, - guest_access: Boolean, - new_users: { - type: Boolean, - default: true - }, - host: String, - api_host: String, - portal_host: String, - n3ds_host: String -}); - -export const ENDPOINT = model('ENDPOINT', endpointSchema); diff --git a/apps/juxtaposition-ui/src/models/endpoint.ts b/apps/juxtaposition-ui/src/models/endpoint.ts new file mode 100644 index 00000000..13ebdf9d --- /dev/null +++ b/apps/juxtaposition-ui/src/models/endpoint.ts @@ -0,0 +1,34 @@ +import { Schema, model } from 'mongoose'; +import type { HydratedDocument } from 'mongoose'; + +export type Endpoint = { + status: number; + server_access_level: string; + topics: boolean; + guest_access: boolean; + new_users: boolean; + host: string; + api_host: string; + portal_host: string; + n3ds_host: string; +} & Document; + +export type HydratedEndpointDocument = HydratedDocument; + +export const endpointSchema = new Schema({ + status: Number, + server_access_level: String, + topics: Boolean, + guest_access: Boolean, + new_users: { + type: Boolean, + default: true + }, + host: String, + api_host: String, + portal_host: String, + n3ds_host: String +}); + +export const Endpoint = model('Endpoint', endpointSchema); +export const ENDPOINT = Endpoint; diff --git a/apps/juxtaposition-ui/src/models/post.js b/apps/juxtaposition-ui/src/models/post.js index 765f2c89..dd59ea05 100644 --- a/apps/juxtaposition-ui/src/models/post.js +++ b/apps/juxtaposition-ui/src/models/post.js @@ -102,6 +102,13 @@ PostSchema.index({ removed: 1 }); +// Index for post count on community page +PostSchema.index({ + community_id: 1, + removed: 1, + parent: 1 +}); + PostSchema.methods.upReply = async function () { const replyCount = this.get('reply_count'); if (replyCount + 1 < 0) { diff --git a/apps/juxtaposition-ui/src/models/report.js b/apps/juxtaposition-ui/src/models/report.js deleted file mode 100644 index 31702486..00000000 --- a/apps/juxtaposition-ui/src/models/report.js +++ /dev/null @@ -1,30 +0,0 @@ -import { Schema, model } from 'mongoose'; - -export const ReportSchema = new Schema({ - pid: Number, - reported_by: Number, - post_id: String, - reason: Number, - message: String, - created_at: { - type: Date, - default: new Date() - }, - resolved: { - type: Boolean, - default: false - }, - note: String, - resolved_by: Number, - resolved_at: Date -}); - -ReportSchema.methods.resolve = async function (pid, note) { - this.set('resolved', true); - this.set('resolved_by', pid); - this.set('resolved_at', new Date()); - this.set('note', note); - await this.save(); -}; - -export const REPORT = model('REPORT', ReportSchema); diff --git a/apps/juxtaposition-ui/src/models/report.ts b/apps/juxtaposition-ui/src/models/report.ts new file mode 100644 index 00000000..1f52b36f --- /dev/null +++ b/apps/juxtaposition-ui/src/models/report.ts @@ -0,0 +1,68 @@ +import { Schema, model } from 'mongoose'; +import type { HydratedDocument, Model } from 'mongoose'; + +export interface IReport { + pid: number; + reported_by: number; + post_id: string; + reason: number; + message: string; + created_at: Date; + resolved?: boolean | null; + note?: string | null; + resolved_by?: number | null; + resolved_at?: Date | null; +} + +export type ReportModel = Model; +export type HydratedReportDocument = HydratedDocument; + +export interface IReportMethods { + resolve(pid: number, note: string | null): Promise; +} + +export const ReportSchema = new Schema({ + pid: { + type: Number, + required: true + }, + reported_by: { + type: Number, + required: true + }, + post_id: { + type: String, + required: true + }, + reason: { + type: Number, + required: true + }, + message: { + type: String, + default: '' + }, + created_at: { + type: Date, + required: true, + default: new Date() + }, + resolved: { + type: Boolean, + default: false + }, + note: String, + resolved_by: Number, + resolved_at: Date +}); + +ReportSchema.method('resolve', async function (pid, note) { + this.set('resolved', true); + this.set('resolved_by', pid); + this.set('resolved_at', new Date()); + this.set('note', note); + await this.save(); +}); + +export const Report = model('Report', ReportSchema); +export const REPORT = Report; diff --git a/apps/juxtaposition-ui/src/models/settings.js b/apps/juxtaposition-ui/src/models/settings.js deleted file mode 100644 index 08a6539b..00000000 --- a/apps/juxtaposition-ui/src/models/settings.js +++ /dev/null @@ -1,110 +0,0 @@ -import { Schema, model } from 'mongoose'; - -export const SettingsSchema = new Schema({ - pid: { type: Number, required: true }, - screen_name: { type: String, required: true }, - account_status: { - type: Number, - default: 0 - }, - // TODO: Move bans to their own collection. User settings should be scoped to older the user - ban_lift_date: { - type: Date, - default: null - }, - banned_by: { - type: Number, - default: null - }, - ban_reason: { - type: String, - default: null - }, - profile_comment: { - type: String, - default: null - }, - profile_comment_visibility: { - type: Boolean, - default: true - }, - game_skill: { - type: Number, - default: 0 - }, - game_skill_visibility: { - type: Boolean, - default: true - }, - birthday_visibility: { - type: Boolean, - default: false - }, - relationship_visibility: { - type: Boolean, - default: false - }, - country_visibility: { - type: Boolean, - default: false - }, - profile_favorite_community_visibility: { - type: Boolean, - default: true - }, - receive_notifications: { - type: Boolean, - default: true - }, - created_at: { - type: Date, - default: Date.now() - }, - last_active: { - type: Date, - default: Date.now(), - index: true - } -}); - -SettingsSchema.methods.updateComment = async function (comment) { - this.set('profile_comment', comment); - await this.save(); -}; - -SettingsSchema.methods.updateSkill = async function (skill) { - this.set('game_skill', skill); - await this.save(); -}; - -SettingsSchema.methods.commentVisible = async function (active) { - this.set('profile_comment_visibility', active); - await this.save(); -}; - -SettingsSchema.methods.skillVisible = async function (active) { - this.set('game_skill_visibility', active); - await this.save(); -}; - -SettingsSchema.methods.birthdayVisible = async function (active) { - this.set('birthday_visibility', active); - await this.save(); -}; - -SettingsSchema.methods.relationshipVisible = async function (active) { - this.set('relationship_visibility', active); - await this.save(); -}; - -SettingsSchema.methods.countryVisible = async function (active) { - this.set('country_visibility', active); - await this.save(); -}; - -SettingsSchema.methods.favCommunityVisible = async function (active) { - this.set('profile_favorite_community_visibility', active); - await this.save(); -}; - -export const SETTINGS = model('SETTINGS', SettingsSchema); diff --git a/apps/juxtaposition-ui/src/models/settings.ts b/apps/juxtaposition-ui/src/models/settings.ts new file mode 100644 index 00000000..f10ed474 --- /dev/null +++ b/apps/juxtaposition-ui/src/models/settings.ts @@ -0,0 +1,168 @@ +import { Schema, model } from 'mongoose'; +import type { HydratedDocument, Model } from 'mongoose'; + +/* This type needs to reflect "reality" as it is in the DB + * Thus, all the optionals, since some legacy documents are missing many fields + */ +export interface ISettings { + pid: number; + screen_name: string; + account_status: number; + ban_lift_date?: Date | null; + banned_by?: number | null; + ban_reason?: string | null; + profile_comment?: string | null; + profile_comment_visibility: boolean; + game_skill: number; + game_skill_visibility: boolean; + birthday_visibility: boolean; + relationship_visibility: boolean; + country_visibility: boolean; + profile_favorite_community_visibility: boolean; + receive_notifications: boolean; + created_at?: Date; + last_active?: Date; +} +// Fields that have "default: " in the Mongoose schema should also be listed here to make them optional +// on input but not output +type SettingsDefaultedFields = + 'account_status' | + 'ban_lift_date' | + 'banned_by' | + 'ban_reason' | + 'profile_comment' | + 'profile_comment_visibility' | + 'game_skill' | + 'game_skill_visibility' | + 'birthday_visibility' | + 'relationship_visibility' | + 'country_visibility' | + 'profile_favorite_community_visibility' | + 'receive_notifications' | + 'created_at' | + 'last_active'; +export type ISettingsInput = Omit & Partial>; + +export interface ISettingsMethods { + updateComment(comment: string | null): Promise; + updateSkill(skill: number): Promise; + commentVisible(active: boolean): Promise; + skillVisible(active: boolean): Promise; + birthdayVisible(active: boolean): Promise; + relationshipVisible(active: boolean): Promise; + countryVisible(active: boolean): Promise; + favCommunityVisible(active: boolean): Promise; +} + +export type SettingsModel = Model; +export type HydratedSettingsDocument = HydratedDocument; + +export const SettingsSchema = new Schema({ + pid: { type: Number, required: true }, + screen_name: { type: String, required: true }, + account_status: { + type: Number, + default: 0 + }, + // TODO: Move bans to their own collection. User settings should be scoped to older the user + ban_lift_date: { + type: Date, + default: null + }, + banned_by: { + type: Number, + default: null + }, + ban_reason: { + type: String, + default: null + }, + profile_comment: { + type: String, + default: null + }, + profile_comment_visibility: { + type: Boolean, + default: true + }, + game_skill: { + type: Number, + default: 0 + }, + game_skill_visibility: { + type: Boolean, + default: true + }, + birthday_visibility: { + type: Boolean, + default: false + }, + relationship_visibility: { + type: Boolean, + default: false + }, + country_visibility: { + type: Boolean, + default: false + }, + profile_favorite_community_visibility: { + type: Boolean, + default: true + }, + receive_notifications: { + type: Boolean, + default: true + }, + created_at: { + type: Date, + default: Date.now() + }, + last_active: { + type: Date, + default: Date.now(), + index: true + } +}); + +SettingsSchema.method('updateComment', async function (comment) { + this.set('profile_comment', comment); + await this.save(); +}); + +SettingsSchema.method('updateSkill', async function (skill) { + this.set('game_skill', skill); + await this.save(); +}); + +SettingsSchema.method('commentVisible', async function (active) { + this.set('profile_comment_visibility', active); + await this.save(); +}); + +SettingsSchema.method('skillVisible', async function (active) { + this.set('game_skill_visibility', active); + await this.save(); +}); + +SettingsSchema.method('birthdayVisible', async function (active) { + this.set('birthday_visibility', active); + await this.save(); +}); + +SettingsSchema.method('relationshipVisible', async function (active) { + this.set('relationship_visibility', active); + await this.save(); +}); + +SettingsSchema.method('countryVisible', async function (active) { + this.set('country_visibility', active); + await this.save(); +}); + +SettingsSchema.method('favCommunityVisible', async function (active) { + this.set('profile_favorite_community_visibility', active); + await this.save(); +}); + +export const Settings = model('Setting', SettingsSchema); +export const SETTINGS = Settings; diff --git a/apps/juxtaposition-ui/src/redisCache.js b/apps/juxtaposition-ui/src/redisCache.ts similarity index 61% rename from apps/juxtaposition-ui/src/redisCache.js rename to apps/juxtaposition-ui/src/redisCache.ts index d1e0f75e..3f6c04c0 100644 --- a/apps/juxtaposition-ui/src/redisCache.js +++ b/apps/juxtaposition-ui/src/redisCache.ts @@ -15,27 +15,32 @@ redisClient.on('connect', () => { logger.success('Redis connected'); }); -export async function redisSet(key, value, expireTime) { +export async function redisSet(key: string, value: string, expireTime?: number): Promise { if (!redisClient.isOpen) { return false; } - await redisClient.set(key, value, 'EX', expireTime); - // Seems to be a library bug, so we have to manually set the expire time - await redisClient.expire(key, expireTime); + await redisClient.set(key, value, { + expiration: expireTime + ? { + type: 'EX', + value: expireTime + } + : undefined + }); return true; } -export async function redisGet(key) { +export async function redisGet(key: string): Promise { if (!redisClient.isOpen) { - return false; + return null; } const result = await redisClient.get(key); return result; } -export async function redisRemove(key) { +export async function redisRemove(key: string): Promise { if (!redisClient.isOpen) { return false; } diff --git a/apps/juxtaposition-ui/src/server.js b/apps/juxtaposition-ui/src/server.ts similarity index 79% rename from apps/juxtaposition-ui/src/server.js rename to apps/juxtaposition-ui/src/server.ts index 0b5dbd87..05be32cb 100644 --- a/apps/juxtaposition-ui/src/server.js +++ b/apps/juxtaposition-ui/src/server.ts @@ -4,6 +4,7 @@ import cookieParser from 'cookie-parser'; import session from 'express-session'; import { RedisStore } from 'connect-redis'; import 'express-async-errors'; // See package docs +import methodOverride from 'method-override'; import { database } from '@/database'; import { logger } from '@/logger'; import { loggerHttp } from '@/loggerHttp'; @@ -16,9 +17,11 @@ import { distFolder } from '@/util'; import { initImageProcessing } from '@/images'; import { loginWall } from '@/middleware/webAuth'; import { listenMetrics, registerMetrics } from '@/metrics'; +import type { NextFunction, Request, Response } from 'express'; +import type { FetchError } from '@/fetch'; // TODO is this used anywhere? -BigInt.prototype['toJSON'] = function () { +(BigInt as any).prototype['toJSON'] = function (): string { return this.toString(); }; @@ -38,7 +41,6 @@ app.disable('x-powered-by'); app.set('view engine', 'ejs'); app.set('views', path.join(distFolder, '/webfiles')); app.set('trust proxy', config.http.trustProxy); -app.get('/ip', (request, response) => response.send(request.ip)); // Create router logger.info('Setting up Middleware'); @@ -53,13 +55,13 @@ app.use(express.urlencoded({ })); app.use(cookieParser()); +app.use(methodOverride('X-HTTP-Method-Override')); app.use(session({ store: new RedisStore({ client: redisClient }), secret: config.aesKey, resave: false, - saveUninitialized: false, - ttl: 60 + saveUninitialized: false })); // import the servers into one @@ -70,38 +72,36 @@ logger.info('Creating 404 status handler'); app.use((req, res) => { req.log.warn('Page not found'); res.status(404); - res.render(req.directory + '/error.ejs', { + return res.renderError({ code: 404, - message: 'Page not found', - id: req.id + message: 'Page not found' }); }); // non-404 error handler logger.info('Creating non-404 status handler'); -app.use((error, req, res, next) => { +app.use((error: Error | FetchError, req: Request, res: Response, next: NextFunction) => { if (res.headersSent) { return next(error); } // small hack because token expiry is weird - if (error.status === 401 && req.directory === 'web') { + if ('status' in error && error.status === 401 && req.directory === 'web') { return loginWall(req, res); } - const status = error.status || 500; + const status = 'status' in error ? error.status ?? 500 : 500; res.status(status); req.log.error(error, 'Request failed!'); - res.render(req.directory + '/error.ejs', { + return res.renderError({ code: status, - message: 'Error', - id: req.id + message: 'Error' }); }); // Starts the server -async function main() { +async function main(): Promise { // Starts the server logger.info('Starting server'); diff --git a/apps/juxtaposition-ui/src/services/juxt-web/index.js b/apps/juxtaposition-ui/src/services/juxt-web/index.ts similarity index 100% rename from apps/juxtaposition-ui/src/services/juxt-web/index.js rename to apps/juxtaposition-ui/src/services/juxt-web/index.ts diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/admin/admin.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/admin/admin.js deleted file mode 100644 index 502028be..00000000 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/admin/admin.js +++ /dev/null @@ -1,663 +0,0 @@ -import crypto from 'crypto'; -import express from 'express'; -import moment from 'moment'; -import multer from 'multer'; -import { deletePostById, getPostById, getPostsByPoster } from '@/api/post'; -import { database } from '@/database'; -import { uploadHeaders, uploadIcons } from '@/images'; -import { logger } from '@/logger'; -import { COMMUNITY } from '@/models/communities'; -import { POST } from '@/models/post'; -import { SETTINGS } from '@/models/settings'; -import { humanDate, createLogEntry, getCommunityHash, getReasonMap, getUserAccountData, getUserHash, newNotification, updateCommunityHash } from '@/util'; -import { getUserMetrics } from '@/metrics'; -const storage = multer.memoryStorage(); -const upload = multer({ storage: storage }); -export const adminRouter = express.Router(); - -adminRouter.get('/posts', async function (req, res) { - if (!res.locals.moderator) { - return res.redirect('/titles/show'); - } - - const reports = await database.getAllOpenReports(); - const communityMap = await getCommunityHash(); - const userContent = await database.getUserContent(req.pid); - const userMap = getUserHash(); - const postIDs = reports.map(obj => obj.post_id); - - const posts = await POST.aggregate([ - { $match: { id: { $in: postIDs } } }, - { - $addFields: { - __order: { $indexOfArray: [postIDs, '$id'] } - } - }, - { $sort: { __order: 1 } }, - { $project: { index: 0, _id: 0 } } - ]); - - res.render(req.directory + '/reports.ejs', { - moment: moment, - userMap, - communityMap, - userContent, - reports, - posts - }); -}); - -async function tryGetSettingsFromPidString(pid_string) { - // Check the string is all digits - if (!/^\d+$/g.test(pid_string)) { - return null; - } - - // Parse it - const pid = parseInt(pid_string, 10); - if (isNaN(pid)) { - return null; - } - - // Get the user doc - const user = await database.getUserSettings(pid); - if (user === null) { - return null; - } - - return user; -} - -adminRouter.get('/accounts', async function (req, res) { - if (!res.locals.moderator) { - return res.redirect('/titles/show'); - } - - const page = req.query.page ? parseInt(req.query.page) : 0; - const search = req.query.search?.trim(); - const limit = 20; - - const users = await (async () => { - if (!search) { - return database.getUsersSettings(limit, page * limit); - } - const results = []; - - const pid_user = await tryGetSettingsFromPidString(search); - if (pid_user !== null) { - results.push(pid_user); - } - - const miis = await database.getUserSettingsFuzzySearch(search, limit, page * limit); - results.push(...miis); - - return results; - })(); - - const userMap = await getUserHash(); - const userMetrics = await getUserMetrics(); - - res.render(req.directory + '/users.ejs', { - moment: moment, - userMap, - users, - page, - search, - userCount: userMetrics.totalUsers, - activeUsers: userMetrics.currentOnlineUsers - }); -}); - -adminRouter.get('/accounts/:pid', async function (req, res) { - if (!res.locals.moderator) { - return res.redirect('/titles/show'); - } - const pnid = await getUserAccountData(req.params.pid).catch((e) => { - logger.error(e, `Could not fetch userdata for ${req.params.pid}`); - }); - const userContent = await database.getUserContent(req.params.pid); - if (isNaN(req.params.pid) || !pnid || !userContent) { - return res.redirect('/404'); - } - const userSettings = await database.getUserSettings(req.params.pid); - const posts = (await getPostsByPoster(req.tokens, req.params.pid, 0)).items; - const communityMap = await getCommunityHash(); - const userMap = getUserHash(); - const reasonMap = getReasonMap(); - - const reports = await database.getReportsByOffender(req.params.pid, 0, 50); - const submittedReports = await database.getReportsByReporter(req.params.pid, 0, 50); - const postIDs = reports.concat(submittedReports).map(obj => obj.post_id); - - const postsMap = await POST.aggregate([ - { $match: { id: { $in: postIDs } } }, - { - $addFields: { - __order: { $indexOfArray: [postIDs, '$id'] } - } - }, - { $sort: { __order: 1 } }, - { $project: { index: 0, _id: 0 } } - ]); - - const removedPosts = await POST.find({ pid: req.params.pid, removed: true }).sort({ removed_at: -1 }).limit(50); - - const auditLog = await database.getLogsForTarget(req.params.pid, 0, 50); - - res.render(req.directory + '/moderate_user.ejs', { - moment: moment, - userSettings, - userContent, - pnid, - - posts, - removedPosts, - - reports, - submittedReports, - - userMap, - communityMap, - postsMap, - reasonMap, - - auditLog - }); -}); - -adminRouter.post('/accounts/:pid', async (req, res) => { - if (!res.locals.moderator) { - return res.redirect('/titles/show'); - } - - const { pid } = req.params; - const oldUserSettings = await database.getUserSettings(pid); - - if (!oldUserSettings) { - res.json({ - error: true - }); - return; - } - - if (req.body.ban_lift_date == '' || req.body.account_status == 0) { - req.body.ban_lift_date = null; - } - - await SETTINGS.findOneAndUpdate({ pid: pid }, { - account_status: req.body.account_status, - ban_lift_date: req.body.ban_lift_date, - banned_by: req.pid, - ban_reason: req.body.ban_reason - }); - - res.json({ - error: false - }); - - if (req.body.account_status === 1) { - await newNotification({ - pid: pid, - type: 'notice', - text: `You have been Limited from Posting until ${humanDate(req.body.ban_lift_date)}. ` + - (req.body.ban_reason ? `Reason: "${req.body.ban_reason}". ` : '') + - `Click this message to view the Juxtaposition Code of Conduct. ` + - `If you have any questions, please contact the moderators on the Pretendo Network Forum (https://preten.do/ban-appeal/).`, - image: '/images/bandwidthalert.png', - link: '/titles/2551084080/new' - }); - } - - let action = 'UPDATE_USER'; - const accountStatusChanged = oldUserSettings.account_status !== req.body.account_status; - const changes = []; - const fields = []; - - if (accountStatusChanged) { - const oldStatus = getAccountStatus(oldUserSettings.account_status); - const newStatus = getAccountStatus(req.body.account_status); - - switch (req.body.account_status) { - case 0: - action = 'UNBAN'; - break; - case 1: - action = 'LIMIT_POSTING'; - break; - case 2: - action = 'TEMP_BAN'; - break; - case 3: - action = 'PERMA_BAN'; - break; - default: - action = 'PERMA_BAN'; - break; - } - fields.push('account_status'); - changes.push(`Account Status changed from "${oldStatus}" to "${newStatus}"`); - } - - if (accountStatusChanged || oldUserSettings.ban_lift_date !== req.body.ban_lift_date) { - fields.push('ban_lift_date'); - changes.push(`User Ban Lift Date changed from "${humanDate(oldUserSettings.ban_lift_date)}" to "${humanDate(req.body.ban_lift_date)}"`); - } - - if (accountStatusChanged || oldUserSettings.ban_reason !== req.body.ban_reason) { - fields.push('ban_reason'); - changes.push(`Ban reason changed from "${oldUserSettings.ban_reason}" to "${req.body.ban_reason}"`); - } - - if (changes.length > 0) { - await createLogEntry( - req.pid, - action, - pid, - changes.join('\n'), - fields - ); - } -}); - -adminRouter.delete('/:reportID', async function (req, res) { - if (!res.locals.moderator) { - return res.sendStatus(401); - } - - const report = await database.getReportById(req.params.reportID); - if (!report) { - return res.sendStatus(402); - } - const post = await getPostById(req.tokens, report.post_id); - if (post === null) { - return res.sendStatus(404); - } - const reason = req.query.reason ? req.query.reason : 'Removed by moderator'; - await deletePostById(req.tokens, post.id, reason); - await report.resolve(req.pid, reason); - - const postType = post.parent ? 'comment' : 'post'; - - await newNotification({ - pid: post.pid, - type: 'notice', - text: `Your ${postType} "${post.id}" has been removed` + - (reason ? ` for the following reason: "${reason}". ` : '. ') + - `Click this message to view the Juxtaposition Code of Conduct. ` + - `If you have any questions, please contact the moderators on the Pretendo Network Forum (https://preten.do/juxt-mods/).`, - image: '/images/bandwidthalert.png', - link: '/titles/2551084080/new' - }); - - await createLogEntry( - req.pid, - 'REMOVE_POST', - post.id, - `Post ${post.id} removed for: "${reason}"` - ); - - return res.sendStatus(200); -}); - -adminRouter.put('/:reportID', async function (req, res) { - if (!res.locals.moderator) { - return res.sendStatus(401); - } - - const report = await database.getReportById(req.params.reportID); - if (!report) { - return res.sendStatus(402); - } - - await report.resolve(req.pid, req.query.reason); - - await createLogEntry( - req.pid, - 'IGNORE_REPORT', - report.id, - `Report ${report.id} ignored for: "${req.query.reason}"` - ); - - return res.sendStatus(200); -}); - -adminRouter.get('/communities', async function (req, res) { - if (!res.locals.developer) { - return res.redirect('/titles/show'); - } - - const page = req.query.page ? parseInt(req.query.page) : 0; - const search = req.query.search; - const limit = 20; - - const communities = search ? await database.getCommunitiesFuzzySearch(search, limit, page * limit) : await database.getCommunities(limit, page * limit); - - res.render(req.directory + '/manage_communities.ejs', { - moment: moment, - communities, - page, - search - }); -}); - -adminRouter.get('/communities/new', async function (req, res) { - if (!res.locals.developer) { - return res.redirect('/titles/show'); - } - - res.render(req.directory + '/new_community.ejs', { - moment: moment - }); -}); - -adminRouter.post('/communities/new', upload.fields([{ name: 'browserIcon', maxCount: 1 }, { name: 'CTRbrowserHeader', maxCount: 1 }, { name: 'WiiUbrowserHeader', maxCount: 1 }]), async (req, res) => { - if (!res.locals.developer) { - return res.redirect('/titles/show'); - } - const communityId = await generateCommunityUID(); - if (!req.files || !req.files.browserIcon || !req.files.CTRbrowserHeader || !req.files.WiiUbrowserHeader) { - return res.sendStatus(422); - } - - const icons = await uploadIcons({ - icon: req.files.browserIcon[0].buffer, - communityId - }); - const headers = await uploadHeaders({ - ctr_header: req.files.CTRbrowserHeader[0].buffer, - wup_header: req.files.WiiUbrowserHeader[0].buffer, - communityId - }); - if (icons === null || headers === null) { - return res.sendStatus(422); - } - - req.body.has_shop_page = req.body.has_shop_page === 'on' ? 1 : 0; - req.body.is_recommended = req.body.is_recommended === 'on' ? 1 : 0; - - const document = { - platform_id: req.body.platform, - name: req.body.name, - description: req.body.description, - open: true, - allows_comments: true, - type: req.body.type, - parent: req.body.parent === 'null' || req.body.parent.trim() === '' ? null : req.body.parent, - owner: req.pid, - created_at: moment(new Date()), - empathy_count: 0, - followers: 0, - has_shop_page: req.body.has_shop_page, - icon: icons.tgaBlob, - ctr_header: headers.ctr, - wup_header: headers.wup, - title_id: req.body.title_ids.replace(/ /g, '').split(','), - community_id: communityId, - olive_community_id: communityId, - is_recommended: req.body.is_recommended, - app_data: req.body.app_data - }; - const newCommunity = new COMMUNITY(document); - await newCommunity.save(); - res.redirect(`/admin/communities/${communityId}`); - - updateCommunityHash(document); - - const communityType = getCommunityType(document.type); - const communityPlatform = getCommunityPlatform(document.platform_id); - const changes = []; - - changes.push(`Name set to "${document.name}"`); - changes.push(`Description set to "${document.description}"`); - changes.push(`Platform ID set to "${communityPlatform}"`); - changes.push(`Type set to "${communityType}"`); - changes.push(`Title IDs set to "${document.title_id.join(', ')}"`); - changes.push(`Parent set to "${document.parent}"`); - changes.push(`App data set to "${document.app_data}"`); - changes.push(`Is Recommended set to "${document.is_recommended}"`); - changes.push(`Has Shop Page set to "${document.has_shop_page}"`); - - const fields = [ - 'name', - 'description', - 'platform_id', - 'type', - 'title_id', - 'browserIcon', - 'CTRbrowserHeader', - 'WiiUbrowserHeader', - 'parent', - 'app_data', - 'is_recommended', - 'has_shop_page' - ]; - await createLogEntry( - req.pid, - 'MAKE_COMMUNITY', - communityId, - changes.join('\n'), - fields - ); -}); - -adminRouter.get('/communities/:community_id', async function (req, res) { - if (!res.locals.developer) { - return res.redirect('/titles/show'); - } - - const community = await COMMUNITY.findOne({ olive_community_id: req.params.community_id }).exec(); - - if (!community) { - return res.redirect('/titles/show'); - } - - res.render(req.directory + '/edit_community.ejs', { - moment: moment, - community - }); -}); - -adminRouter.post('/communities/:id', upload.fields([{ name: 'browserIcon', maxCount: 1 }, { - name: 'CTRbrowserHeader', - maxCount: 1 -}, { name: 'WiiUbrowserHeader', maxCount: 1 }]), async (req, res) => { - if (!res.locals.developer) { - return res.redirect('/titles/show'); - } - - JSON.parse(JSON.stringify(req.files)); - const communityId = req.params.id; - - const oldCommunity = await COMMUNITY.findOne({ olive_community_id: communityId }).exec(); - - if (!oldCommunity) { - return res.redirect('/404'); - } - - // browser icon - let icons = null; - if (req.files.browserIcon) { - icons = await uploadIcons({ - icon: req.files.browserIcon[0].buffer, - communityId - }); - if (icons === null) { - return res.sendStatus(422); - } - } - // 3DS / Wii U Header - let headers = null; - if (req.files.CTRbrowserHeader && req.files.WiiUbrowserHeader) { - headers = await uploadHeaders({ - ctr_header: req.files.CTRbrowserHeader[0].buffer, - wup_header: req.files.WiiUbrowserHeader[0].buffer, - communityId - }); - if (headers === null) { - return res.sendStatus(422); - } - } - - req.body.has_shop_page = req.body.has_shop_page === 'on' ? 1 : 0; - req.body.is_recommended = req.body.is_recommended === 'on' ? 1 : 0; - - const document = { - type: req.body.type, - has_shop_page: req.body.has_shop_page, - platform_id: req.body.platform, - icon: icons?.tgaBlob ?? oldCommunity.icon, - ctr_header: headers?.ctr ?? oldCommunity.ctr_header, - wup_header: headers?.wup ?? oldCommunity.wup_header, - title_id: req.body.title_ids.replace(/ /g, '').split(','), - parent: req.body.parent === 'null' || req.body.parent.trim() === '' ? null : req.body.parent, - app_data: req.body.app_data, - is_recommended: req.body.is_recommended, - name: req.body.name, - description: req.body.description - }; - await COMMUNITY.findOneAndUpdate({ olive_community_id: communityId }, { $set: document }, { upsert: true }).exec(); - - res.redirect(`/admin/communities/${communityId}`); - - updateCommunityHash(document); - - // determine the changes made to the community - const changes = []; - const fields = []; - - if (oldCommunity.name !== document.name) { - fields.push('name'); - changes.push(`Name changed from "${oldCommunity.name}" to "${document.name}"`); - } - if (oldCommunity.description !== document.description) { - fields.push('description'); - changes.push(`Description changed from "${oldCommunity.description}" to "${document.description}"`); - } - if (oldCommunity.platform_id !== parseInt(document.platform_id)) { - const oldCommunityPlatform = getCommunityPlatform(oldCommunity.platform_id); - const newCommunityPlatform = getCommunityPlatform(document.platform_id); - fields.push('platform_id'); - changes.push(`Platform ID changed from "${oldCommunityPlatform}" to "${newCommunityPlatform}"`); - } - if (oldCommunity.type !== parseInt(document.type)) { - const oldCommunityType = getCommunityType(oldCommunity.type); - const newCommunityType = getCommunityType(document.type); - fields.push('type'); - changes.push(`Type changed from "${oldCommunityType}" to "${newCommunityType}"`); - } - if (oldCommunity.title_id.toString() !== document.title_id.toString()) { - fields.push('title_id'); - changes.push(`Title IDs changed from "${oldCommunity.title_id.join(', ')}" to "${document.title_id.join(', ')}"`); - } - if (req.files.browserIcon) { - fields.push('browserIcon'); - changes.push('Icon changed'); - } - if (req.files.CTRbrowserHeader) { - fields.push('CTRbrowserHeader'); - changes.push('3DS Banner changed'); - } - if (req.files.WiiUbrowserHeader) { - fields.push('WiiUbrowserHeader'); - changes.push('Wii U Banner changed'); - } - if (oldCommunity.parent !== document.parent) { - fields.push('parent'); - changes.push(`Parent changed from "${oldCommunity.parent}" to "${document.parent}"`); - } - if (oldCommunity.app_data !== document.app_data) { - fields.push('app_data'); - changes.push(`App data changed from "${oldCommunity.app_data}" to "${document.app_data}"`); - } - if (oldCommunity.is_recommended !== document.is_recommended) { - fields.push('is_recommended'); - changes.push(`Is Recommended changed from "${oldCommunity.is_recommended}" to "${document.is_recommended}"`); - } - if (oldCommunity.has_shop_page !== document.has_shop_page) { - fields.push('has_shop_page'); - changes.push(`Has Shop Page changed from "${oldCommunity.has_shop_page}" to "${document.has_shop_page}"`); - } - - if (changes.length > 0) { - await createLogEntry( - req.pid, - 'UPDATE_COMMUNITY', - oldCommunity.olive_community_id, - changes.join('\n'), - fields - ); - } -}); - -adminRouter.delete('/communities/:id', async (req, res) => { - if (!res.locals.developer) { - return res.redirect('/titles/show'); - } - - const { id } = req.params; - - await COMMUNITY.findByIdAndRemove(id).exec(); - - res.json({ - error: false - }); - - await createLogEntry( - req.pid, - 'DELETE_COMMUNITY', - id, - `Community ${id} deleted` - ); -}); - -async function generateCommunityUID(length) { - let id = crypto.getRandomValues(new Uint32Array(1)).toString().substring(0, length); - const inuse = await COMMUNITY.findOne({ community_id: id }); - id = (inuse ? await generateCommunityUID(length) : id); - return id; -} - -function getAccountStatus(status) { - switch (status) { - case 0: - return 'Normal'; - case 1: - return 'Limited from Posting'; - case 2: - return 'Temporary Ban'; - case 3: - return 'Permanent Ban'; - default: - return `Unknown (${status})`; - } -} - -function getCommunityType(type) { - type = Number(type); - switch (type) { - case 0: - return 'Main'; - case 1: - return 'Sub'; - case 2: - return 'Announcement'; - case 3: - return 'Private'; - default: - return `Unknown (${type})`; - } -} - -function getCommunityPlatform(platform_id) { - platform_id = Number(platform_id); - switch (platform_id) { - case 0: - return 'Wii U'; - case 1: - return '3DS'; - case 2: - return 'Both'; - default: - return `Unknown (${platform_id})`; - } -} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/admin/admin.tsx b/apps/juxtaposition-ui/src/services/juxt-web/routes/admin/admin.tsx new file mode 100644 index 00000000..1f34135b --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/admin/admin.tsx @@ -0,0 +1,769 @@ +import crypto from 'crypto'; +import express from 'express'; +import multer from 'multer'; +import { z } from 'zod'; +import { deletePostById, getPostById } from '@/api/post'; +import { database } from '@/database'; +import { uploadHeaders, uploadIcons } from '@/images'; +import { logger } from '@/logger'; +import { COMMUNITY } from '@/models/communities'; +import { POST } from '@/models/post'; +import { SETTINGS } from '@/models/settings'; +import { humanDate, createLogEntry, getReasonMap, getUserAccountData, newNotification, updateCommunityHash } from '@/util'; +import { getUserMetrics } from '@/metrics'; +import { parseReq } from '@/services/juxt-web/routes/routeUtils'; +import { WebUserListView } from '@/services/juxt-web/views/web/admin/userListView'; +import { buildContext } from '@/services/juxt-web/views/context'; +import { WebReportListView } from '@/services/juxt-web/views/web/admin/reportListView'; +import { WebManageCommunityView } from '@/services/juxt-web/views/web/admin/manageCommunityView'; +import { WebNewCommunityView } from '@/services/juxt-web/views/web/admin/newCommunityView'; +import { WebEditCommunityView } from '@/services/juxt-web/views/web/admin/editCommunityView'; +import { WebModerateUserView } from '@/services/juxt-web/views/web/admin/moderateUserView'; +import type { ReportWithPost } from '@/services/juxt-web/views/web/admin/reportListView'; +import type { HydratedSettingsDocument } from '@/models/settings'; +import type { HydratedReportDocument } from '@/models/report'; +const storage = multer.memoryStorage(); +const upload = multer({ storage: storage }); +export const adminRouter = express.Router(); + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- Too difficult to type +const onOffSchema = () => z.enum(['on', 'off']).default('off').transform(v => v === 'on' ? 1 : 0); + +adminRouter.get('/posts', async function (req, res) { + if (!res.locals.moderator) { + return res.redirect('/titles/show'); + } + const { auth } = parseReq(req); + + // `any` is needed because database.js is not typed yet + const rawReports: HydratedReportDocument[] = await database.getAllOpenReports() as any; + const userContent = await database.getUserContent(auth().pid); + if (!userContent) { + throw new Error('User content is null'); + } + + const postIds = rawReports.map(obj => obj.post_id); + const nonRemovedPosts = await POST.find( + { id: { $in: postIds }, removed: false } + ); + const postMap = new Map(nonRemovedPosts.map(p => [p.id, p])); + const reportsWithPost = rawReports.map(v => ({ + report: v, + post: postMap.get(v.post_id) + })); + const reports: ReportWithPost[] = []; + reportsWithPost.forEach((v) => { + if (v.post) { + reports.push({ + post: v.post, + report: v.report + }); + } + }); + + res.jsxForDirectory({ + web: + }); +}); + +async function tryGetSettingsFromPidString(pid_string: string): Promise { + // Check the string is all digits + if (!/^\d+$/g.test(pid_string)) { + return null; + } + + // Parse it + const pid = parseInt(pid_string, 10); + if (isNaN(pid)) { + return null; + } + + // Get the user doc + const user = await database.getUserSettings(pid); + if (user === null) { + return null; + } + + return user; +} + +adminRouter.get('/accounts', async function (req, res) { + if (!res.locals.moderator) { + return res.redirect('/titles/show'); + } + + const { query } = parseReq(req, { + query: z.object({ + page: z.coerce.number().default(0), + search: z.string().trim().optional() + }) + }); + + const page = query.page; + const search = query.search; + const limit = 20; + + const users = await (async (): Promise => { + if (!search) { + return database.getUsersSettings(limit, page * limit); + } + const results = []; + + const pid_user = await tryGetSettingsFromPidString(search); + if (pid_user !== null) { + results.push(pid_user); + } + + const miis = await database.getUserSettingsFuzzySearch(search, limit, page * limit); + results.push(...miis); + + return results; + })(); + + const userMetrics = await getUserMetrics(); + + res.jsxForDirectory({ + web: + }); +}); + +adminRouter.get('/accounts/:pid', async function (req, res) { + if (!res.locals.moderator) { + return res.redirect('/titles/show'); + } + + const { params } = parseReq(req, { + params: z.object({ + pid: z.coerce.number() + }) + }); + + const reqPid = params.pid; + + const pnid = await getUserAccountData(reqPid).catch((e) => { + logger.error(e, `Could not fetch userdata for ${reqPid}`); + }); + const userContent = await database.getUserContent(reqPid); + const userSettings = await database.getUserSettings(reqPid); + if (isNaN(reqPid) || !pnid || !userContent || !userSettings) { + return res.redirect('/404'); + } + + // `any` is needed because database.js is not typed yet + const reports: HydratedReportDocument[] = await database.getReportsByOffender(reqPid, 0, 50) as any; + const submittedReports: HydratedReportDocument[] = await database.getReportsByReporter(reqPid, 0, 50) as any; + const postIDs = reports.concat(submittedReports).map(obj => obj.post_id); + + const postsMap = await POST.aggregate([ + { $match: { id: { $in: postIDs } } }, + { + $addFields: { + __order: { $indexOfArray: [postIDs, '$id'] } + } + }, + { $sort: { __order: 1 } }, + { $project: { index: 0, _id: 0 } } + ]); + + const removedPosts = await POST.find({ pid: reqPid, removed: true }).sort({ removed_at: -1 }).limit(50); + + const auditLog = await database.getLogsForTarget(reqPid, 0, 50); + + res.jsxForDirectory({ + web: ( + + ) + }); +}); + +adminRouter.post('/accounts/:pid', async (req, res) => { + if (!res.locals.moderator) { + return res.redirect('/titles/show'); + } + + const { params, body, auth } = parseReq(req, { + params: z.object({ + pid: z.coerce.number() + }), + body: z.object({ + // Empty string counts as null + ban_lift_date: z.string().trim().nullable().transform(v => v === '' ? null : v) + .pipe(z.iso.datetime().nullable()), + account_status: z.number(), + ban_reason: z.string().trim().nullable().transform(v => v === '' ? null : v) + }).transform((v) => { + if (v.account_status == 0) { + v.ban_lift_date = null; // If account status is normal, remove ban date + } + return v; + }) + }); + + const pid = params.pid; + const oldUserSettings = await database.getUserSettings(pid); + + if (!oldUserSettings) { + res.json({ + error: true + }); + return; + } + + await SETTINGS.findOneAndUpdate({ pid: pid }, { + account_status: body.account_status, + ban_lift_date: body.ban_lift_date, + banned_by: req.pid, + ban_reason: body.ban_reason + }); + + res.json({ + error: false + }); + + if (body.account_status === 1) { + await newNotification({ + pid: pid, + type: 'notice', + text: `You have been Limited from Posting until ${humanDate(body.ban_lift_date)}. ` + + (body.ban_reason ? `Reason: "${body.ban_reason}". ` : '') + + `Click this message to view the Juxtaposition Code of Conduct. ` + + `If you have any questions, please contact the moderators on the Pretendo Network Forum (https://preten.do/ban-appeal/).`, + image: '/images/bandwidthalert.png', + link: '/titles/2551084080/new' + }); + } + + let action = 'UPDATE_USER'; + const accountStatusChanged = oldUserSettings.account_status !== body.account_status; + const changes = []; + const fields = []; + + if (accountStatusChanged) { + const oldStatus = getAccountStatus(oldUserSettings.account_status); + const newStatus = getAccountStatus(body.account_status); + + switch (body.account_status) { + case 0: + action = 'UNBAN'; + break; + case 1: + action = 'LIMIT_POSTING'; + break; + case 2: + action = 'TEMP_BAN'; + break; + case 3: + action = 'PERMA_BAN'; + break; + default: + action = 'PERMA_BAN'; + break; + } + fields.push('account_status'); + changes.push(`Account Status changed from "${oldStatus}" to "${newStatus}"`); + } + + if (accountStatusChanged || oldUserSettings.ban_lift_date !== body.ban_lift_date) { + fields.push('ban_lift_date'); + changes.push(`User Ban Lift Date changed from "${humanDate(oldUserSettings.ban_lift_date)}" to "${humanDate(body.ban_lift_date)}"`); + } + + if (accountStatusChanged || oldUserSettings.ban_reason !== body.ban_reason) { + fields.push('ban_reason'); + changes.push(`Ban reason changed from "${oldUserSettings.ban_reason}" to "${body.ban_reason}"`); + } + + if (changes.length > 0) { + await createLogEntry( + auth().pid, + action, + pid.toString(), + changes.join('\n'), + fields + ); + } +}); + +adminRouter.delete('/:reportID', async function (req, res) { + if (!res.locals.moderator) { + return res.sendStatus(401); + } + + const { params, query, auth } = parseReq(req, { + params: z.object({ + reportID: z.string() + }), + query: z.object({ + reason: z.string().trim().optional() + }) + }); + + // `any` is needed because database.js is not typed yet + const report: HydratedReportDocument | null = await database.getReportById(params.reportID) as any; + if (!report) { + return res.sendStatus(402); + } + const post = await getPostById(auth().tokens, report.post_id); + if (post === null) { + return res.sendStatus(404); + } + const reason = query.reason ?? 'Removed by moderator'; + await deletePostById(auth().tokens, post.id, reason); + await report.resolve(auth().pid, reason); + + const postType = post.parent ? 'comment' : 'post'; + + await newNotification({ + pid: post.pid, + type: 'notice', + text: `Your ${postType} "${post.id}" has been removed` + + (reason ? ` for the following reason: "${reason}". ` : '. ') + + `Click this message to view the Juxtaposition Code of Conduct. ` + + `If you have any questions, please contact the moderators on the Pretendo Network Forum (https://preten.do/juxt-mods/).`, + image: '/images/bandwidthalert.png', + link: '/titles/2551084080/new' + }); + + await createLogEntry( + auth().pid, + 'REMOVE_POST', + post.id, + `Post ${post.id} removed for: "${reason}"` + ); + + return res.sendStatus(200); +}); + +adminRouter.put('/:reportID', async function (req, res) { + if (!res.locals.moderator) { + return res.sendStatus(401); + } + + const { params, query, auth } = parseReq(req, { + params: z.object({ + reportID: z.string() + }), + query: z.object({ + reason: z.string().trim().optional() + }) + }); + + // `any` is needed because database.js is not typed yet + const report: HydratedReportDocument | null = await database.getReportById(params.reportID) as any; + if (!report) { + return res.sendStatus(402); + } + + await report.resolve(auth().pid, query.reason ?? null); + + await createLogEntry( + auth().pid, + 'IGNORE_REPORT', + report.id, + `Report ${report.id} ignored for: "${query.reason ?? 'No reason provided'}"` + ); + + return res.sendStatus(200); +}); + +adminRouter.get('/communities', async function (req, res) { + if (!res.locals.developer) { + return res.redirect('/titles/show'); + } + + const { query } = parseReq(req, { + query: z.object({ + page: z.coerce.number().default(0), + search: z.string().trim().optional() + }) + }); + + const page = query.page; + const search = query.search; + const limit = 20; + + const communities = search ? await database.getCommunitiesFuzzySearch(search, limit, page * limit) : await database.getCommunities(limit, page * limit); + + res.jsxForDirectory({ + web: + }); +}); + +adminRouter.get('/communities/new', async function (req, res) { + if (!res.locals.developer) { + return res.redirect('/titles/show'); + } + + res.jsxForDirectory({ + web: + }); +}); + +adminRouter.post('/communities/new', upload.fields([{ name: 'browserIcon', maxCount: 1 }, { name: 'CTRbrowserHeader', maxCount: 1 }, { name: 'WiiUbrowserHeader', maxCount: 1 }]), async (req, res) => { + if (!res.locals.developer) { + return res.redirect('/titles/show'); + } + + const { body, files, auth } = parseReq(req, { + body: z.object({ + has_shop_page: onOffSchema(), + is_recommended: onOffSchema(), + platform: z.string().trim(), + name: z.string().trim(), + description: z.string().trim(), + type: z.coerce.number().min(0).max(3), + parent: z.string().trim().nullable().transform(v => v === 'null' || v === '' ? null : v), + title_ids: z.string().trim() + .transform(v => v.replaceAll(' ', '').split(',').filter(v => v.length > 0)) + .pipe(z.array(z.string().min(1))), + app_data: z.string().trim() + }), + files: ['browserIcon', 'CTRbrowserHeader', 'WiiUbrowserHeader'] + }); + + const communityId = await generateCommunityUID(); + if (files.browserIcon.length === 0 || files.CTRbrowserHeader.length === 0 || files.WiiUbrowserHeader.length === 0) { + return res.sendStatus(422); + } + + const icons = await uploadIcons({ + icon: files.browserIcon[0].buffer, + communityId + }); + const headers = await uploadHeaders({ + ctr_header: files.CTRbrowserHeader[0].buffer, + wup_header: files.WiiUbrowserHeader[0].buffer, + communityId + }); + if (icons === null || headers === null) { + return res.sendStatus(422); + } + + const document = { + platform_id: body.platform, + name: body.name, + description: body.description, + open: true, + allows_comments: true, + type: body.type, + parent: body.parent, + owner: auth().pid, + created_at: new Date(), + empathy_count: 0, + followers: 0, + has_shop_page: body.has_shop_page, + icon: icons.tgaBlob, + ctr_header: headers.ctr, + wup_header: headers.wup, + title_id: body.title_ids, + community_id: communityId, + olive_community_id: communityId, + is_recommended: body.is_recommended, + app_data: body.app_data + }; + const newCommunity = new COMMUNITY(document); + await newCommunity.save(); + res.redirect(`/admin/communities/${communityId}`); + + updateCommunityHash(newCommunity); + + const communityType = getCommunityType(document.type); + const communityPlatform = getCommunityPlatform(document.platform_id); + const changes = []; + + changes.push(`Name set to "${document.name}"`); + changes.push(`Description set to "${document.description}"`); + changes.push(`Platform ID set to "${communityPlatform}"`); + changes.push(`Type set to "${communityType}"`); + changes.push(`Title IDs set to "${document.title_id.join(', ')}"`); + changes.push(`Parent set to "${document.parent}"`); + changes.push(`App data set to "${document.app_data}"`); + changes.push(`Is Recommended set to "${document.is_recommended}"`); + changes.push(`Has Shop Page set to "${document.has_shop_page}"`); + + const fields = [ + 'name', + 'description', + 'platform_id', + 'type', + 'title_id', + 'browserIcon', + 'CTRbrowserHeader', + 'WiiUbrowserHeader', + 'parent', + 'app_data', + 'is_recommended', + 'has_shop_page' + ]; + await createLogEntry( + auth().pid, + 'MAKE_COMMUNITY', + communityId, + changes.join('\n'), + fields + ); +}); + +adminRouter.get('/communities/:community_id', async function (req, res) { + if (!res.locals.developer) { + return res.redirect('/titles/show'); + } + + const { params } = parseReq(req, { + params: z.object({ + community_id: z.string() + }) + }); + + const community = await COMMUNITY.findOne({ olive_community_id: params.community_id }).exec(); + + if (!community) { + return res.redirect('/titles/show'); + } + + res.jsxForDirectory({ + web: + }); +}); + +adminRouter.post('/communities/:id', upload.fields([{ name: 'browserIcon', maxCount: 1 }, { + name: 'CTRbrowserHeader', + maxCount: 1 +}, { name: 'WiiUbrowserHeader', maxCount: 1 }]), async (req, res) => { + if (!res.locals.developer) { + return res.redirect('/titles/show'); + } + + const { body, params, files, auth } = parseReq(req, { + params: z.object({ + id: z.string() + }), + body: z.object({ + has_shop_page: onOffSchema(), + is_recommended: onOffSchema(), + platform: z.string().trim(), + name: z.string().trim(), + description: z.string().trim(), + type: z.coerce.number().min(0).max(3), + parent: z.string().trim().nullable().transform(v => v === 'null' || v === '' ? null : v), + title_ids: z.string().trim() + .transform(v => v.replaceAll(' ', '').split(',').filter(v => v.length > 0)) + .pipe(z.array(z.string().min(1))), + app_data: z.string().trim() + }), + files: ['browserIcon', 'CTRbrowserHeader', 'WiiUbrowserHeader'] + }); + + JSON.parse(JSON.stringify(files)); // wtf does this do? + const communityId = params.id; + + const oldCommunity = await COMMUNITY.findOne({ olive_community_id: communityId }).exec(); + + if (!oldCommunity) { + return res.redirect('/404'); + } + + // browser icon + let icons = null; + if (files.browserIcon.length > 0) { + icons = await uploadIcons({ + icon: files.browserIcon[0].buffer, + communityId + }); + if (icons === null) { + return res.sendStatus(422); + } + } + // 3DS / Wii U Header + let headers = null; + if (files.CTRbrowserHeader.length > 0 && files.WiiUbrowserHeader.length > 0) { + headers = await uploadHeaders({ + ctr_header: files.CTRbrowserHeader[0].buffer, + wup_header: files.WiiUbrowserHeader[0].buffer, + communityId + }); + if (headers === null) { + return res.sendStatus(422); + } + } + + const document = { + type: body.type, + has_shop_page: body.has_shop_page, + platform_id: body.platform, + icon: icons?.tgaBlob ?? oldCommunity.icon, + ctr_header: headers?.ctr ?? oldCommunity.ctr_header, + wup_header: headers?.wup ?? oldCommunity.wup_header, + title_id: body.title_ids, + parent: body.parent, + app_data: body.app_data, + is_recommended: body.is_recommended, + name: body.name, + description: body.description + }; + const comm = await COMMUNITY.findOneAndUpdate({ olive_community_id: communityId }, { $set: document }, { upsert: true }).exec(); + if (!comm) { + throw new Error('Can not find community after update'); + } + + res.redirect(`/admin/communities/${communityId}`); + + updateCommunityHash(comm); + + // determine the changes made to the community + const changes = []; + const fields = []; + + if (oldCommunity.name !== comm.name) { + fields.push('name'); + changes.push(`Name changed from "${oldCommunity.name}" to "${comm.name}"`); + } + if (oldCommunity.description !== comm.description) { + fields.push('description'); + changes.push(`Description changed from "${oldCommunity.description}" to "${comm.description}"`); + } + if (oldCommunity.platform_id !== comm.platform_id) { + const oldCommunityPlatform = getCommunityPlatform(oldCommunity.platform_id); + const newCommunityPlatform = getCommunityPlatform(comm.platform_id); + fields.push('platform_id'); + changes.push(`Platform ID changed from "${oldCommunityPlatform}" to "${newCommunityPlatform}"`); + } + if (oldCommunity.type !== comm.type) { + const oldCommunityType = getCommunityType(oldCommunity.type); + const newCommunityType = getCommunityType(comm.type); + fields.push('type'); + changes.push(`Type changed from "${oldCommunityType}" to "${newCommunityType}"`); + } + if (oldCommunity.title_id.toString() !== comm.title_id.toString()) { + fields.push('title_id'); + changes.push(`Title IDs changed from "${oldCommunity.title_id.join(', ')}" to "${comm.title_id.join(', ')}"`); + } + if (files.browserIcon.length > 0) { + fields.push('browserIcon'); + changes.push('Icon changed'); + } + if (files.CTRbrowserHeader.length > 0) { + fields.push('CTRbrowserHeader'); + changes.push('3DS Banner changed'); + } + if (files.WiiUbrowserHeader.length > 0) { + fields.push('WiiUbrowserHeader'); + changes.push('Wii U Banner changed'); + } + if (oldCommunity.parent !== comm.parent) { + fields.push('parent'); + changes.push(`Parent changed from "${oldCommunity.parent}" to "${comm.parent}"`); + } + if (oldCommunity.app_data !== comm.app_data) { + fields.push('app_data'); + changes.push(`App data changed from "${oldCommunity.app_data}" to "${comm.app_data}"`); + } + if (oldCommunity.is_recommended !== comm.is_recommended) { + fields.push('is_recommended'); + changes.push(`Is Recommended changed from "${oldCommunity.is_recommended}" to "${comm.is_recommended}"`); + } + if (oldCommunity.has_shop_page !== comm.has_shop_page) { + fields.push('has_shop_page'); + changes.push(`Has Shop Page changed from "${oldCommunity.has_shop_page}" to "${comm.has_shop_page}"`); + } + + if (changes.length > 0) { + await createLogEntry( + auth().pid, + 'UPDATE_COMMUNITY', + oldCommunity.olive_community_id, + changes.join('\n'), + fields + ); + } +}); + +adminRouter.delete('/communities/:id', async (req, res) => { + if (!res.locals.developer) { + return res.redirect('/titles/show'); + } + + const { params, auth } = parseReq(req, { + params: z.object({ + id: z.string() + }) + }); + + await COMMUNITY.findByIdAndDelete(params.id).exec(); + + res.json({ + error: false + }); + + await createLogEntry( + auth().pid, + 'DELETE_COMMUNITY', + params.id, + `Community ${params.id} deleted` + ); +}); + +async function generateCommunityUID(length?: number | undefined): Promise { + let id = crypto.getRandomValues(new Uint32Array(1)).toString().substring(0, length); + const inuse = await COMMUNITY.findOne({ community_id: id }); + id = (inuse ? await generateCommunityUID(length) : id); + return id; +} + +function getAccountStatus(status: number): string { + switch (status) { + case 0: + return 'Normal'; + case 1: + return 'Limited from Posting'; + case 2: + return 'Temporary Ban'; + case 3: + return 'Permanent Ban'; + default: + return `Unknown (${status})`; + } +} + +function getCommunityType(type: number | string): string { + const parsedType = Number(type); + switch (parsedType) { + case 0: + return 'Main'; + case 1: + return 'Sub'; + case 2: + return 'Announcement'; + case 3: + return 'Private'; + default: + return `Unknown (${parsedType})`; + } +} + +function getCommunityPlatform(platform_id: number | string): string { + const parsedPlatformId = Number(platform_id); + switch (parsedPlatformId) { + case 0: + return 'Wii U'; + case 1: + return '3DS'; + case 2: + return 'Both'; + default: + return `Unknown (${parsedPlatformId})`; + } +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/communities.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/communities.js deleted file mode 100644 index 2cb0db49..00000000 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/communities.js +++ /dev/null @@ -1,244 +0,0 @@ -import express from 'express'; -import moment from 'moment'; -import multer from 'multer'; -import { config } from '@/config'; -import { database } from '@/database'; -import { COMMUNITY } from '@/models/communities'; -import { POST } from '@/models/post'; -import { redisGet, redisSet } from '@/redisCache'; -import { getCommunityHash } from '@/util'; -const upload = multer({ dest: 'uploads/' }); -export const communitiesRouter = express.Router(); - -communitiesRouter.get('/', async function (req, res) { - const newCommunities = JSON.parse(await redisGet('newCommunities')) || await database.getNewCommunities(6); - let popularCommunities = JSON.parse(await redisGet('popularCommunities')); - - if (!popularCommunities) { - const last24Hours = await calculateMostPopularCommunities(); - popularCommunities = await COMMUNITY.aggregate([ - { $match: { olive_community_id: { $in: last24Hours }, parent: null } }, - { - $addFields: { - index: { $indexOfArray: [last24Hours, '$olive_community_id'] } - } - }, - { $sort: { index: 1 } }, - { $limit: 9 }, - { $project: { index: 0, _id: 0 } } - ]); - redisSet('popularCommunities', JSON.stringify(popularCommunities), 60 * 60); - redisSet('newCommunities', JSON.stringify(newCommunities), 60 * 60); - } - - res.render(req.directory + '/communities.ejs', { - cache: true, - popularCommunities: popularCommunities, - newCommunities: newCommunities - }); -}); - -communitiesRouter.get('/all', async function (req, res) { - const communities = await database.getCommunities(90); - res.render(req.directory + '/all_communities.ejs', { - communities: communities - }); -}); - -communitiesRouter.get('/:communityID', async function (req, res) { - if (req.query.title_id) { - const community = await database.getCommunityByTitleID(req.query.title_id); - if (!community) { - return res.redirect('/404'); - } - return res.redirect(`/titles/${community.olive_community_id}/new`); - } - res.redirect(`/titles/${req.params.communityID}/new`); -}); - -communitiesRouter.get('/:communityID/related', async function (req, res) { - const userSettings = await database.getUserSettings(req.pid); - const userContent = await database.getUserContent(req.pid); - if (!userContent || !userSettings) { - return res.redirect('/404'); - } - const community = await database.getCommunityByID(req.params.communityID.toString()); - if (!community) { - return res.render(req.directory + '/error.ejs', { code: 404, message: 'Community not Found' }); - } - const communityMap = await getCommunityHash(); - const children = await database.getSubCommunities(community.olive_community_id); - if (!children) { - return res.redirect(`/titles/${community.olive_community_id}/new`); - } - - res.render(req.directory + '/sub_communities.ejs', { - selection: 2, - communityMap, - community, - children - }); -}); - -communitiesRouter.get('/:communityID/:type', async function (req, res) { - const userSettings = await database.getUserSettings(req.pid); - const userContent = await database.getUserContent(req.pid); - if (!userContent || !userSettings) { - return res.redirect('/404'); - } - const community = await database.getCommunityByID(req.params.communityID.toString()); - if (!community) { - return res.render(req.directory + '/error.ejs', { code: 404, message: 'Community not Found' }); - } - - if (!community.permissions) { - community.permissions = { - open: community.open, - minimum_new_post_access_level: 0, - minimum_new_comment_access_level: 0, - minimum_new_community_access_level: 0 - }; - await community.save(); - } - const communityMap = await getCommunityHash(); - let children = await database.getSubCommunities(community.olive_community_id); - if (children.length === 0) { - children = null; - } - let posts; - let type; - - if (req.params.type === 'hot') { - posts = await database.getNumberPopularCommunityPostsByID(community, config.postLimit); - type = 1; - } else if (req.params.type === 'verified') { - posts = await database.getNumberVerifiedCommunityPostsByID(community, config.postLimit); - type = 2; - } else { - posts = await database.getNewPostsByCommunity(community, config.postLimit); - type = 0; - } - const numPosts = await database.getTotalPostsByCommunity(community); - - const bundle = { - posts, - open: community.permissions.open, - numPosts, - communityMap, - userContent, - link: `/titles/${req.params.communityID}/${req.params.type}/more?offset=${posts.length}&pjax=true` - }; - - if (req.query.pjax) { - return res.render(req.directory + '/partials/posts_list.ejs', { - bundle, - moment - }); - } - - res.render(req.directory + '/community.ejs', { - // EJS variable and server-side variable - moment: moment, - community: community, - communityMap: communityMap, - posts: posts, - totalNumPosts: numPosts, - userSettings: userSettings, - userContent: userContent, - pnid: req.user, - children, - type, - bundle, - template: 'posts_list' - }); -}); - -communitiesRouter.get('/:communityID/:type/more', async function (req, res) { - let offset = parseInt(req.query.offset); - const userContent = await database.getUserContent(req.pid); - const communityMap = await getCommunityHash(); - let posts; - const community = await database.getCommunityByID(req.params.communityID); - if (!community) { - return res.redirect('/404'); - } - if (!offset) { - offset = 0; - } - switch (req.params.type) { - case 'hot': - posts = await database.getNumberPopularCommunityPostsByID(community, config.postLimit, offset); - break; - case 'verified': - posts = await database.getNumberVerifiedCommunityPostsByID(community, config.postLimit, offset); - break; - default: - posts = await database.getNewPostsByCommunity(community, config.postLimit, offset); - break; - } - - const bundle = { - posts, - open: true, - numPosts: posts.length, - communityMap, - userContent, - link: `/titles/${req.params.communityID}/${req.params.type}/more?offset=${offset + posts.length}&pjax=true` - }; - - if (posts.length > 0) { - res.render(req.directory + '/partials/posts_list.ejs', { - communityMap: communityMap, - moment: moment, - database: database, - bundle - }); - } else { - res.sendStatus(204); - } -}); - -communitiesRouter.post('/follow', upload.none(), async function (req, res) { - const community = await database.getCommunityByID(req.body.id); - const userContent = await database.getUserContent(req.pid); - const popularCommunities = JSON.parse(await redisGet('popularCommunities')); - let updated = false; - - if (userContent !== null && userContent.followed_communities.indexOf(community.olive_community_id) === -1) { - community.upFollower(); - userContent.addToCommunities(community.olive_community_id); - res.send({ status: 200, id: community.olive_community_id, count: community.followers }); - updated = true; - } else if (userContent !== null && userContent.followed_communities.indexOf(community.olive_community_id) !== -1) { - community.downFollower(); - userContent.removeFromCommunities(community.olive_community_id); - res.send({ status: 200, id: community.olive_community_id, count: community.followers }); - updated = true; - } else { - res.send({ status: 423, id: community.olive_community_id, count: community.followers }); - } - - if (popularCommunities && updated) { - const index = popularCommunities.findIndex(element => element.olive_community_id === community.olive_community_id); - if (index !== -1) { - popularCommunities[index].followers = community.followers; - redisSet('popularCommunities', JSON.stringify(popularCommunities), 60 * 60); - } - } -}); - -async function calculateMostPopularCommunities() { - const now = new Date(); - const last24Hours = new Date(now.getTime() - 24 * 60 * 60 * 1000); - - const posts = await POST.find({ created_at: { $gte: last24Hours }, message_to_pid: null }).lean(); - - const communityIds = {}; - for (const post of posts) { - const communityId = post.community_id; - communityIds[communityId] = (communityIds[communityId] || 0) + 1; - } - return Object.entries(communityIds) - .sort((a, b) => b[1] - a[1]) - .map(entry => entry[0]); -} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/communities.tsx b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/communities.tsx new file mode 100644 index 00000000..27cd8574 --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/communities.tsx @@ -0,0 +1,352 @@ +import express from 'express'; +import multer from 'multer'; +import { z } from 'zod'; +import { config } from '@/config'; +import { database } from '@/database'; +import { COMMUNITY } from '@/models/communities'; +import { POST } from '@/models/post'; +import { redisGet, redisRemove, redisSet } from '@/redisCache'; +import { parseReq } from '@/services/juxt-web/routes/routeUtils'; +import { WebCommunityListView, WebCommunityOverviewView } from '@/services/juxt-web/views/web/communityListView'; +import { buildContext } from '@/services/juxt-web/views/context'; +import { PortalCommunityListView, PortalCommunityOverviewView } from '@/services/juxt-web/views/portal/communityListView'; +import { CtrCommunityListView, CtrCommunityOverviewView } from '@/services/juxt-web/views/ctr/communityListView'; +import { PortalSubCommunityView } from '@/services/juxt-web/views/portal/subCommunityView'; +import { CtrSubCommunityView } from '@/services/juxt-web/views/ctr/subCommunityView'; +import { WebCommunityView } from '@/services/juxt-web/views/web/communityView'; +import { PortalCommunityView } from '@/services/juxt-web/views/portal/communityView'; +import { CtrCommunityView } from '@/services/juxt-web/views/ctr/communityView'; +import { WebPostListView } from '@/services/juxt-web/views/web/postList'; +import { PortalPostListView } from '@/services/juxt-web/views/portal/postList'; +import { CtrPostListView } from '@/services/juxt-web/views/ctr/postList'; +import { zodFallback } from '@/util'; +import type { InferSchemaType } from 'mongoose'; +import type { PostListViewProps } from '@/services/juxt-web/views/web/postList'; +import type { CommunityViewProps } from '@/services/juxt-web/views/web/communityView'; +import type { SubCommunityViewProps } from '@/services/juxt-web/views/portal/subCommunityView'; +import type { CommunityListViewProps, CommunityOverviewViewProps } from '@/services/juxt-web/views/web/communityListView'; +import type { CommunitySchema } from '@/models/communities'; +const upload = multer({ dest: 'uploads/' }); +export const communitiesRouter = express.Router(); + +communitiesRouter.get('/', async function (req, res) { + const communityStats = await getCommunityStats(); + + const props: CommunityOverviewViewProps = { + ctx: buildContext(res), + newCommunities: communityStats.new, + popularCommunities: communityStats.popular + }; + res.jsxForDirectory({ + web: , + portal: , + ctr: + }); +}); + +communitiesRouter.get('/all', async function (req, res) { + const communities = await database.getCommunities(90); + + const props: CommunityListViewProps = { + ctx: buildContext(res), + communities + }; + res.jsxForDirectory({ + web: , + portal: , + ctr: + }); +}); + +communitiesRouter.get('/:communityID', async function (req, res) { + const { query, params } = parseReq(req, { + query: z.object({ + title_id: z.string().optional() + }), + params: z.object({ + communityID: z.string().regex(/^[0-9]+$/).or(zodFallback(null)) + }) + }); + + if (query.title_id) { + const community = await database.getCommunityByTitleID(query.title_id); + if (!community) { + return res.redirect('/404'); + } + return res.redirect(`/titles/${community.olive_community_id}/new`); + } + + if (!params.communityID) { + return res.redirect('/404'); + } + return res.redirect(`/titles/${params.communityID}/new`); +}); + +communitiesRouter.get('/:communityID/related', async function (req, res) { + const { params, auth } = parseReq(req, { + params: z.object({ + communityID: z.string() + }) + }); + + const userSettings = await database.getUserSettings(auth().pid); + const userContent = await database.getUserContent(auth().pid); + if (!userContent || !userSettings) { + return res.redirect('/404'); + } + const community = await database.getCommunityByID(params.communityID); + if (!community) { + return res.renderError({ + code: 404, + message: 'Community not Found' + }); + } + const children = await database.getSubCommunities(community.olive_community_id); + if (!children) { + return res.redirect(`/titles/${community.olive_community_id}/new`); + } + + const props: SubCommunityViewProps = { + ctx: buildContext(res), + community, + subcommunities: children + }; + res.jsxForDirectory({ + portal: , + ctr: + }); +}); + +communitiesRouter.get('/:communityID/:type', async function (req, res) { + const { query, params, auth } = parseReq(req, { + params: z.object({ + communityID: z.string(), + type: z.string() + }), + query: z.object({ + pjax: z.stringbool().optional() + }) + }); + + const userSettings = await database.getUserSettings(auth().pid); + const userContent = await database.getUserContent(auth().pid); + if (!userContent || !userSettings) { + return res.redirect('/404'); + } + const community = await database.getCommunityByID(params.communityID); + if (!community) { + return res.renderError({ + code: 404, + message: 'Community not Found' + }); + } + + if (!community.permissions) { + community.permissions = { + open: community.open, + minimum_new_post_access_level: 0, + minimum_new_comment_access_level: 0, + minimum_new_community_access_level: 0 + }; + await community.save(); + } + const canPost = ( + (community.permissions.open && community.type < 2) || + (community.admins && community.admins.indexOf(auth().pid) !== -1) || + (auth().user.accessLevel >= community.permissions.minimum_new_post_access_level) + ) && userSettings.account_status === 0; + const isUserFollowing = userContent.followed_communities.includes(community.olive_community_id); + + const subCommunities = await database.getSubCommunities(community.olive_community_id); + let posts; + let type; + + if (params.type === 'hot') { + posts = await database.getNumberPopularCommunityPostsByID(community, config.postLimit); + type = 1; + } else if (params.type === 'verified') { + posts = await database.getNumberVerifiedCommunityPostsByID(community, config.postLimit); + type = 2; + } else { + posts = await database.getNewPostsByCommunity(community, config.postLimit); + type = 0; + } + const numPosts = await database.getTotalPostsByCommunity(community); + + const postListProps: PostListViewProps = { + ctx: buildContext(res), + nextLink: `/titles/${params.communityID}/${params.type}/more?offset=${posts.length}&pjax=true`, + posts, + userContent + }; + + if (query.pjax) { + return res.jsxForDirectory({ + web: , + portal: , + ctr: + }); + } + + const props: CommunityViewProps = { + ctx: buildContext(res), + feedType: type, + community, + hasSubCommunities: subCommunities.length > 0, + totalPosts: numPosts, + canPost, + isUserFollowing + }; + res.jsxForDirectory({ + web: ( + + + + ), + portal: ( + + + + ), + ctr: ( + + + + ) + }); +}); + +communitiesRouter.get('/:communityID/:type/more', async function (req, res) { + const { query, params, auth } = parseReq(req, { + params: z.object({ + communityID: z.string(), + type: z.string() + }), + query: z.object({ + offset: z.coerce.number().optional().default(0) + }) + }); + + const offset = query.offset; + const userContent = await database.getUserContent(auth().pid); + let posts; + const community = await database.getCommunityByID(params.communityID); + if (!community || !userContent) { + return res.redirect('/404'); + } + switch (params.type) { + case 'hot': + posts = await database.getNumberPopularCommunityPostsByID(community, config.postLimit, offset); + break; + case 'verified': + posts = await database.getNumberVerifiedCommunityPostsByID(community, config.postLimit, offset); + break; + default: + posts = await database.getNewPostsByCommunity(community, config.postLimit, offset); + break; + } + + const postListProps: PostListViewProps = { + ctx: buildContext(res), + nextLink: `/titles/${params.communityID}/${params.type}/more?offset=${offset + posts.length}&pjax=true`, + posts, + userContent + }; + + if (posts.length === 0) { + return res.sendStatus(204); + } + + return res.jsxForDirectory({ + web: , + portal: , + ctr: + }); +}); + +communitiesRouter.post('/follow', upload.none(), async function (req, res) { + const { body, auth } = parseReq(req, { + body: z.object({ + id: z.string() + }) + }); + + const community = await database.getCommunityByID(body.id); + const userContent = await database.getUserContent(auth().pid); + + if (!userContent || !community) { + res.send({ status: 423, id: community?.olive_community_id, count: community?.followers }); + return; + } + + // Pretty terrible use of `any` here, but database models aren't typed yet so I have to + const userFollowsCommunity = userContent.followed_communities.includes(community.olive_community_id); + if (!userFollowsCommunity) { + (community as any).upFollower(); + (userContent as any).addToCommunities(community.olive_community_id); + } else { + (community as any).downFollower(); + (userContent as any).removeFromCommunities(community.olive_community_id); + } + + await redisRemove('popularCommunities'); // Force cache to refresh + res.send({ status: 200, id: community.olive_community_id, count: community.followers }); +}); + +type CommunityStats = { + popular: InferSchemaType[]; + new: InferSchemaType[]; +}; + +async function getCommunityStats(): Promise { + const cachedRedisPopularCommunities = await redisGet('popularCommunities'); + let popularCommunities: InferSchemaType[] | null = cachedRedisPopularCommunities ? JSON.parse(cachedRedisPopularCommunities) : null; + if (popularCommunities == null) { + const last24Hours = await calculateMostPopularCommunities(); + popularCommunities = await COMMUNITY.aggregate([ + { $match: { olive_community_id: { $in: last24Hours }, parent: null } }, + { + $addFields: { + index: { $indexOfArray: [last24Hours, '$olive_community_id'] } + } + }, + { $sort: { index: 1 } }, + { $limit: 9 }, + { $project: { index: 0, _id: 0 } } + ]); + await redisSet('popularCommunities', JSON.stringify(popularCommunities), 60 * 60); + } + + const cachedRedisNewCommunities = await redisGet('newCommunities'); + let newCommunities: InferSchemaType[] = cachedRedisNewCommunities ? JSON.parse(cachedRedisNewCommunities) : null; + if (newCommunities == null) { + newCommunities = await database.getNewCommunities(6); + await redisSet('newCommunities', JSON.stringify(newCommunities), 60 * 60); + } + + return { + popular: popularCommunities, + new: newCommunities + }; +} + +async function calculateMostPopularCommunities(): Promise { + const now = new Date(); + const last24Hours = new Date(now.getTime() - 24 * 60 * 60 * 1000); + + const posts = await POST.find({ created_at: { $gte: last24Hours }, message_to_pid: null }).lean(); + + const communityIds: Record = {}; + for (const post of posts) { + const communityId = post.community_id; + if (!communityId) { + continue; + } + + communityIds[communityId] ??= 0; + communityIds[communityId] += 1; + } + return Object.entries(communityIds) + .sort((a, b) => b[1] - a[1]) + .map(entry => entry[0]); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/feed.tsx b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/feed.tsx index f32b201a..7affcff4 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/feed.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/feed.tsx @@ -3,7 +3,7 @@ import { z } from 'zod'; import { database } from '@/database'; import { POST } from '@/models/post'; import { config } from '@/config'; -import { parseReq } from '@/services/juxt-web/routes/routeUtils'; +import { ifNotMod, parseReq } from '@/services/juxt-web/routes/routeUtils'; import { WebGlobalFeedView, WebPersonalFeedView } from '@/services/juxt-web/views/web/feed'; import { buildContext } from '@/services/juxt-web/views/context'; import { WebPostListView } from '@/services/juxt-web/views/web/postList'; @@ -54,10 +54,13 @@ feedRouter.get('/all', async function (req, res) { if (!userContent) { return res.redirect('/404'); } + const posts = await POST.find({ - parent: null, - message_to_pid: null, - removed: false + ...ifNotMod(res, { + parent: null, + removed: false + }), + message_to_pid: null }).limit(config.postLimit).sort({ created_at: -1 }); const nextLink = `/feed/all/more?offset=${posts.length}&pjax=true`; @@ -117,9 +120,11 @@ feedRouter.get('/all/more', async function (req, res) { } const posts = await POST.find({ - parent: null, - message_to_pid: null, - removed: false + ...ifNotMod(res, { + parent: null, + removed: false + }), + message_to_pid: null }).skip(query.offset).limit(config.postLimit).sort({ created_at: -1 }); const nextLink = `/feed/all/more?offset=${query.offset + posts.length}&pjax=true`; diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/messages.tsx b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/messages.tsx index 8c447e1b..398f23bd 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/messages.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/messages.tsx @@ -88,7 +88,7 @@ messagesRouter.post('/new', async function (req, res) { }); if (paintingBlob === null) { res.status(422); - return res.render(req.directory + '/error.ejs', { + return res.renderError({ code: 422, message: 'Upload failed. Please try again later.' }); @@ -103,7 +103,7 @@ messagesRouter.post('/new', async function (req, res) { }); if (screenshots === null) { res.status(422); - return res.render(req.directory + '/error.ejs', { + return res.renderError({ code: 422, message: 'Upload failed. Please try again later.' }); diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/notifications.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/notifications.tsx similarity index 62% rename from apps/juxtaposition-ui/src/services/juxt-web/routes/console/notifications.js rename to apps/juxtaposition-ui/src/services/juxt-web/routes/console/notifications.tsx index f48c7528..35c61f73 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/notifications.js +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/notifications.tsx @@ -1,18 +1,31 @@ import express from 'express'; import moment from 'moment'; +import { z } from 'zod'; import { database } from '@/database'; import { getUserHash, getUserFriendRequestsIncoming } from '@/util'; +import { parseReq } from '@/services/juxt-web/routes/routeUtils'; export const notificationRouter = express.Router(); notificationRouter.get('/my_news', async function (req, res) { - const notifications = await database.getNotifications(req.pid, 25, 0); + const { query, auth } = parseReq(req, { + query: z.object({ + pjax: z.stringbool().optional() + }) + }); + + const notifications = await database.getNotifications(auth().pid, 25, 0); const userMap = getUserHash(); const bundle = { notifications, userMap }; - if (req.query.pjax) { + for (const notif of notifications.filter(noti => noti.read === false)) { + // Pretty terrible use of `any` here, but database models aren't typed yet so I have to + await (notif as any).markRead(); + } + + if (query.pjax) { return res.render(req.directory + '/partials/notifications.ejs', { bundle, moment @@ -25,13 +38,16 @@ notificationRouter.get('/my_news', async function (req, res) { bundle, template: 'notifications' }); - notifications.filter(noti => noti.read === false).forEach(function (notification) { - notification.markRead(); - }); }); notificationRouter.get('/friend_requests', async function (req, res) { - let requests = (await getUserFriendRequestsIncoming(req.pid)).reverse(); + const { query, auth } = parseReq(req, { + query: z.object({ + pjax: z.stringbool().optional() + }) + }); + + let requests = (await getUserFriendRequestsIncoming(auth().pid)).reverse(); const now = new Date(); requests = requests.filter(request => new Date(Number(request.expires) * 1000) > new Date(now.getTime() - 29 * 24 * 60 * 60 * 1000)); const userMap = getUserHash(); @@ -40,7 +56,7 @@ notificationRouter.get('/friend_requests', async function (req, res) { userMap }; - if (req.query.pjax) { + if (query.pjax) { return res.render(req.directory + '/partials/requests.ejs', { bundle, moment diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/posts.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/posts.tsx similarity index 56% rename from apps/juxtaposition-ui/src/services/juxt-web/routes/console/posts.js rename to apps/juxtaposition-ui/src/services/juxt-web/routes/console/posts.tsx index 8cede055..fa844fb4 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/posts.js +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/posts.tsx @@ -1,8 +1,8 @@ import crypto from 'crypto'; import express from 'express'; -import rateLimit from 'express-rate-limit'; -import moment from 'moment'; +import { rateLimit } from 'express-rate-limit'; import multer from 'multer'; +import { z } from 'zod'; import { deletePostById, getPostById, getPostsByParentId } from '@/api/post'; import { config } from '@/config'; import { database } from '@/database'; @@ -11,8 +11,21 @@ import { logger } from '@/logger'; import { POST } from '@/models/post'; import { REPORT } from '@/models/report'; import { redisRemove } from '@/redisCache'; -import { createLogEntry, getCommunityHash, getInvalidPostRegex, getUserAccountData, newNotification } from '@/util'; +import { createLogEntry, getInvalidPostRegex, getUserAccountData } from '@/util'; import { addEmpathyById, removeEmpathyById } from '@/api/empathy'; +import { parseReq } from '@/services/juxt-web/routes/routeUtils'; +import { buildContext } from '@/services/juxt-web/views/context'; +import { WebPostPageView } from '@/services/juxt-web/views/web/postPageView'; +import { CtrPostPageView } from '@/services/juxt-web/views/ctr/postPageView'; +import { PortalPostPageView } from '@/services/juxt-web/views/portal/postPageView'; +import type { Request, Response } from 'express'; +import type { InferSchemaType } from 'mongoose'; +import type { GetUserDataResponse } from '@pretendonetwork/grpc/account/get_user_data_rpc'; +import type { PostPageViewProps } from '@/services/juxt-web/views/web/postPageView'; +import type { PostSchema } from '@/models/post'; +import type { CommunitySchema } from '@/models/communities'; +import type { HydratedSettingsDocument } from '@/models/settings'; +import type { ContentSchema } from '@/models/content'; const upload = multer({ dest: 'uploads/' }); export const postsRouter = express.Router(); @@ -28,7 +41,7 @@ const postLimit = rateLimit({ } else if (req.body.community_id) { res.redirect('/titles/' + req.body.community_id); } else { - res.render(req.directory + '/error.ejs', { + return res.renderError({ code: 429, message: 'Too many new posts have been created.' }); @@ -41,13 +54,20 @@ const yeahLimit = rateLimit({ max: 10, // Limit each IP to 60 requests per `window` standardHeaders: true, legacyHeaders: true, - message: (req, _res) => { + message: (req: Request) => { return { status: 423, id: req.body.postID, count: 0 }; } }); postsRouter.get('/:post_id/oembed.json', async function (req, res) { - const post = await getPostById(req.tokens, req.params.post_id); + const { params, auth, hasAuth } = parseReq(req, { + params: z.object({ + post_id: z.string() + }) + }); + const maybeTokens = hasAuth() ? auth().tokens : {}; + + const post = await getPostById(maybeTokens, params.post_id); if (!post) { return res.sendStatus(404); } @@ -89,31 +109,28 @@ postsRouter.get('/:post_id/oembed.json', async function (req, res) { }); postsRouter.post('/empathy', yeahLimit, async function (req, res) { - const post = await getPostById(req.tokens, req.body.postID); + const { body, auth } = parseReq(req, { + body: z.object({ + postID: z.string() + }) + }); + + const post = await getPostById(auth().tokens, body.postID); if (!post) { return res.sendStatus(404); } - const existingEmpathy = post.yeahs.indexOf(req.pid) !== -1; + const existingEmpathy = post.yeahs.indexOf(auth().pid) !== -1; const result = !existingEmpathy - ? await addEmpathyById(req.tokens, post.id) - : await removeEmpathyById(req.tokens, post.id); + ? await addEmpathyById(auth().tokens, post.id) + : await removeEmpathyById(auth().tokens, post.id); if (result === null) { res.send({ status: 423, id: post.id, count: post.empathy_count }); + return; } res.send({ status: 200, id: result.post_id, count: result.empathy_count }); - if (result.action === 'add' && req.pid !== post.pid) { - await newNotification({ - pid: post.pid, - type: 'yeah', - objectID: post.id, - userPID: req.pid, - link: `/posts/${post.id}` - }); - } - await redisRemove(`${post.pid}_user_page_posts`); }); @@ -122,51 +139,84 @@ postsRouter.post('/new', postLimit, upload.none(), async function (req, res) { }); postsRouter.get('/:post_id', async function (req, res) { - const userSettings = await database.getUserSettings(req.pid); - const userContent = await database.getUserContent(req.pid); + const { params, hasAuth, auth } = parseReq(req, { + params: z.object({ + post_id: z.string() + }) + }); + const maybeTokens = hasAuth() ? auth().tokens : {}; + + let userSettings: HydratedSettingsDocument | null = null; + let userContent: InferSchemaType | null = null; + if (hasAuth()) { + userSettings = await database.getUserSettings(auth().pid); + userContent = await database.getUserContent(auth().pid); + } - const post = await getPostById(req.tokens, req.params.post_id); + const post = await getPostById(maybeTokens, params.post_id); if (!post) { return res.redirect('/404'); } if (post.parent) { - const parent = await getPostById(req.tokens, post.parent); + const parent = await getPostById(maybeTokens, post.parent); if (!parent) { return res.redirect('/404'); } return res.redirect(`/posts/${parent.id}`); } const community = await database.getCommunityByID(post.community_id); - const communityMap = await getCommunityHash(); - const replies = (await getPostsByParentId(req.tokens, post.id, 0))?.items ?? []; + if (!community) { + return res.redirect('/404'); + } + + // increase limit for post replies since there's no pagination yet + const replies = (await getPostsByParentId(maybeTokens, post.id, 0, 500))?.items ?? []; const postPNID = await getUserAccountData(post.pid); - res.render(req.directory + '/post.ejs', { - moment: moment, - userSettings: userSettings, - userContent: userContent, - post: post, - replies: replies, - community: community, - communityMap: communityMap, + const canPost = hasAuth() && ( + (community.permissions.open && community.type < 2) || + (community.admins && community.admins.indexOf(auth().pid) !== -1) || + (auth().user.accessLevel >= community.permissions.minimum_new_comment_access_level) + ) && userSettings?.account_status === 0; + + const props: PostPageViewProps = { + ctx: buildContext(res), + community, + post, postPNID, - pnid: req.user + replies, + canPost, + userContent + }; + res.jsxForDirectory({ + web: , + ctr: , + portal: }); }); postsRouter.delete('/:post_id', async function (req, res) { - const post = await getPostById(req.tokens, req.params.post_id); + const { params, query, auth } = parseReq(req, { + params: z.object({ + post_id: z.string() + }), + query: z.object({ + reason: z.string().optional() + }) + }); + + const post = await getPostById(auth().tokens, params.post_id); if (!post) { return res.sendStatus(404); } - const result = await deletePostById(req.tokens, post.id, req.query.reason); + const result = await deletePostById(auth().tokens, post.id, query.reason); if (result === null) { return res.sendStatus(404); } // TODO move audit logging to backend - if (res.locals.moderator && req.pid !== post.pid) { - const reason = req.query.reason ? req.query.reason : 'Removed by moderator'; + if (res.locals.moderator && auth().pid !== post.pid) { + const reason = query.reason ? query.reason : 'Removed by moderator'; // TODO Temporarily disabled, moderators need a way to delete without notification // const postType = post.parent ? 'comment' : 'post'; @@ -179,9 +229,9 @@ postsRouter.delete('/:post_id', async function (req, res) { // }); await createLogEntry( - req.pid, + auth().pid, 'REMOVE_POST', - post.pid, + post.pid.toString(), `Post ${post.id} removed for: "${reason}"` ); } @@ -200,20 +250,28 @@ postsRouter.post('/:post_id/new', postLimit, upload.none(), async function (req, }); postsRouter.post('/:post_id/report', upload.none(), async function (req, res) { - const { reason, message, post_id } = req.body; - const post = await getPostById(req.tokens, post_id); + const { body, auth } = parseReq(req, { + body: z.object({ + reason: z.string(), + message: z.string(), + post_id: z.string() + }) + }); + + const { reason, message, post_id } = body; + const post = await getPostById(auth().tokens, post_id); if (!reason || !post_id || !post) { return res.redirect('/404'); } - const duplicate = await database.getDuplicateReports(req.pid, post_id); + const duplicate = await database.getDuplicateReports(auth().pid, post_id); if (duplicate) { return res.redirect(`/posts/${post.id}`); } const reportDoc = { pid: post.pid, - reported_by: req.pid, + reported_by: auth().pid, post_id, reason, message, @@ -226,7 +284,7 @@ postsRouter.post('/:post_id/report', upload.none(), async function (req, res) { return res.redirect(`/posts/${post.id}`); }); -function canPost(community, userSettings, parentPost, user) { +function canPost(community: InferSchemaType, userSettings: HydratedSettingsDocument, parentPost: InferSchemaType | null, user: GetUserDataResponse): boolean { const isReply = !!parentPost; const isPublicPostableCommunity = community.type >= 0 && community.type < 2; const isOpenCommunity = community.permissions.open; @@ -252,20 +310,39 @@ function canPost(community, userSettings, parentPost, user) { return isReply ? isOpenCommunity : isPublicPostableCommunity; } -async function newPost(req, res) { - const userSettings = await database.getUserSettings(req.pid); +async function newPost(req: Request, res: Response): Promise { + const { params, body, auth } = parseReq(req, { + params: z.object({ + post_id: z.string().optional() + }), + body: z.object({ + body: z.string().default(''), + painting: z.string().default(''), + screenshot: z.string().default(''), + community_id: z.string(), + _post_type: z.string(), + bmp: z.stringbool().default(false), + feeling_id: z.coerce.number(), + spoiler: z.stringbool().default(false), + is_app_jumpable: z.stringbool().default(false), + language_id: z.coerce.number().optional() + }) + }); + + const userSettings = await database.getUserSettings(auth().pid); let parentPost = null; const postId = await generatePostUID(21); - let community = await database.getCommunityByID(req.body.community_id); - if (req.params.post_id) { - parentPost = await database.getPostByID(req.params.post_id.toString()); + let community = await database.getCommunityByID(body.community_id); + if (params.post_id) { + parentPost = await database.getPostByID(params.post_id.toString()); if (!parentPost) { - return res.sendStatus(403); + res.sendStatus(403); + return; } else { community = await database.getCommunityByID(parentPost.community_id); } } - if (req.params.post_id && (req.body.body === '' && req.body.painting === '' && req.body.screenshot === '')) { + if (params.post_id && (body.body === '' && body.painting === '' && body.screenshot === '')) { res.status(422); return res.redirect('/posts/' + req.params.post_id.toString()); } @@ -281,40 +358,42 @@ async function newPost(req, res) { } let paintingBlob = null; - if (req.body._post_type === 'painting' && req.body.painting) { + if (body._post_type === 'painting' && body.painting) { paintingBlob = await uploadPainting({ - blob: req.body.painting, + blob: body.painting, autodetectFormat: false, - isBmp: req.body.bmp === 'true', - pid: req.pid, + isBmp: body.bmp, + pid: auth().pid, postId }); if (paintingBlob === null) { res.status(422); - return res.render(req.directory + '/error.ejs', { + res.renderError({ code: 422, message: 'Upload failed. Please try again later.' }); + return; } } let screenshots = null; - if (req.body.screenshot) { + if (body.screenshot) { screenshots = await uploadScreenshot({ - blob: req.body.screenshot, - pid: req.pid, + blob: body.screenshot, + pid: auth().pid, postId }); if (screenshots === null) { res.status(422); - return res.render(req.directory + '/error.ejs', { + res.renderError({ code: 422, message: 'Upload failed. Please try again later.' }); + return; } } let miiFace; - switch (parseInt(req.body.feeling_id)) { + switch (body.feeling_id) { case 1: miiFace = 'smile_open_mouth.png'; break; @@ -334,45 +413,47 @@ async function newPost(req, res) { miiFace = 'normal_face.png'; break; } - const body = req.body.body; - if (body && getInvalidPostRegex().test(body)) { + const postBody = req.body.body; + if (postBody && getInvalidPostRegex().test(postBody)) { // TODO - Log this error - return res.sendStatus(422); + res.sendStatus(422); + return; } - if (body && body.length > 280) { + if (postBody && postBody.length > 280) { // TODO - Log this error - return res.sendStatus(422); + res.sendStatus(422); + return; } const document = { title_id: community.title_id[0], community_id: community.olive_community_id, screen_name: userSettings.screen_name, - body: body, + body: postBody, painting: paintingBlob ?? '', screenshot: screenshots?.full ?? '', screenshot_length: screenshots?.fullLength ?? 0, screenshot_thumb: screenshots?.thumb ?? '', screenshot_aspect: screenshots?.aspect ?? '', - country_id: req.paramPackData ? req.paramPackData.country_id : 49, + country_id: auth().paramPackData?.country_id ?? 49, created_at: new Date(), - feeling_id: req.body.feeling_id, + feeling_id: body.feeling_id, id: postId, is_autopost: 0, - is_spoiler: (req.body.spoiler) ? 1 : 0, - is_app_jumpable: req.body.is_app_jumpable, - language_id: req.body.language_id, - mii: req.user.mii.data, - mii_face_url: `${config.cdnDomain}/mii/${req.user.pid}/${miiFace}`, - pid: req.pid, - platform_id: req.paramPackData ? req.paramPackData.platform_id : 0, - region_id: req.paramPackData ? req.paramPackData.region_id : 2, + is_spoiler: (body.spoiler) ? 1 : 0, + is_app_jumpable: body.is_app_jumpable, + language_id: body.language_id, + mii: auth().user.mii?.data, + mii_face_url: `${config.cdnDomain}/mii/${auth().pid}/${miiFace}`, + pid: auth().pid, + platform_id: auth().paramPackData?.platform_id ?? 0, + region_id: auth().paramPackData?.region_id ?? 2, verified: res.locals.moderator, parent: parentPost ? parentPost.id : null }; - const duplicatePost = await database.getDuplicatePosts(req.pid, document); - if (duplicatePost && req.params.post_id) { - return res.redirect('/posts/' + req.params.post_id.toString()); + const duplicatePost = await database.getDuplicatePosts(auth().pid, document); + if (duplicatePost && params.post_id) { + return res.redirect('/posts/' + params.post_id.toString()); } if ((document.body === '' && document.painting === '' && document.screenshot === '') || duplicatePost) { return res.redirect('/titles/' + community.olive_community_id + '/new'); @@ -383,26 +464,21 @@ async function newPost(req, res) { parentPost.reply_count = parentPost.reply_count + 1; parentPost.save(); } - if (parentPost && (parentPost.pid !== req.user.pid)) { - await newNotification({ - pid: parentPost.pid, - type: 'reply', - user: req.pid, - link: `/posts/${parentPost.id}` - }); - } if (parentPost) { - res.redirect('/posts/' + req.params.post_id.toString()); + if (!params.post_id) { + throw new Error('Has parent post but no postId, this is impossible'); + } + res.redirect('/posts/' + params.post_id.toString()); await redisRemove(`${parentPost.pid}_user_page_posts`); } else { res.redirect('/titles/' + community.olive_community_id + '/new'); - await redisRemove(`${req.pid}_user_page_posts`); + await redisRemove(`${auth().pid}_user_page_posts`); } } -async function generatePostUID(length) { +async function generatePostUID(length: number): Promise { let id = Buffer.from(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(length * 2))), 'binary').toString('base64').replace(/[+/]/g, '').substring(0, length); const inuse = await POST.findOne({ id }); - id = (inuse ? await generatePostUID() : id); + id = (inuse ? await generatePostUID(length) : id); return id; } diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/show.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/show.js deleted file mode 100644 index 9dc03c3f..00000000 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/show.js +++ /dev/null @@ -1,53 +0,0 @@ -import express from 'express'; -import { database } from '@/database'; -import { createUser, setName } from '@/util'; -export const showRouter = express.Router(); - -showRouter.get('/', async function (req, res) { - if (req.pid === 1000000000) { - return res.render(req.directory + '/guest_notice.ejs'); - } - - const user = await database.getUserSettings(req.pid); - const content = await database.getUserContent(req.pid); - if (!user || !content) { - return res.render(req.directory + '/first_run.ejs'); - } - - if (req.query.topic_tag) { - res.redirect(`/topics?topic_tag=${req.query.topic_tag}`); - } else if (req.query.pid) { - res.redirect(`/users/${req.query.pid}`); - } else { - res.redirect('/titles'); - } - - const usrMii = await database.getUserSettings(req.pid); - if (req.user.mii.name !== usrMii.screen_name) { - setName(req.pid, req.user.mii.name); - usrMii.screen_name = req.user.mii.name; - await usrMii.save(); - } -}); - -showRouter.get('/first', async function (req, res) { - res.render(req.directory + '/first_run.ejs'); -}); - -showRouter.post('/newUser', async function (req, res) { - if (req.pid === null || !req.new_users || req.directory === 'web') { - return res.sendStatus(401); - } - - const user = await database.getUserSettings(req.pid); - if (user) { - return res.sendStatus(504); - } - - await createUser(req.pid, req.body.experience, req.body.notifications); - if (await database.getUserSettings(req.pid) !== null) { - res.sendStatus(200); - } else { - res.sendStatus(504); - } -}); diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/show.tsx b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/show.tsx new file mode 100644 index 00000000..638cc1a8 --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/show.tsx @@ -0,0 +1,83 @@ +import express from 'express'; +import { z } from 'zod'; +import { database } from '@/database'; +import { createUser, setName } from '@/util'; +import { parseReq } from '@/services/juxt-web/routes/routeUtils'; +import { buildContext } from '@/services/juxt-web/views/context'; +import { WebFirstRunView } from '@/services/juxt-web/views/web/firstRunView'; +import { PortalFirstRunView } from '@/services/juxt-web/views/portal/firstRunView'; +import { CtrFirstRunView } from '@/services/juxt-web/views/ctr/firstRunView'; + +export const showRouter = express.Router(); + +showRouter.get('/', async function (req, res) { + const { auth, hasAuth, query } = parseReq(req, { + query: z.object({ + topic_tag: z.string().optional(), + pid: z.string().optional() + }) + }); + + const user = await database.getUserSettings(req.pid); + const content = await database.getUserContent(req.pid); + if (!user || !content) { + return res.jsxForDirectory({ + web: , + portal: , + ctr: + }); + } + + if (query.topic_tag) { + res.redirect(`/topics?topic_tag=${query.topic_tag}`); + } else if (query.pid) { + res.redirect(`/users/${query.pid}`); + } else { + res.redirect('/titles'); + } + + if (hasAuth()) { + const currentMii = auth().user.mii; + if (!currentMii || !user) { + return; + } + if (currentMii.name !== user.screen_name) { + setName(auth().pid, currentMii.name); + user.screen_name = currentMii.name; + await user.save(); + } + } +}); + +showRouter.get('/first', async function (req, res) { + return res.jsxForDirectory({ + web: , + portal: , + ctr: + }); +}); + +showRouter.post('/newUser', async function (req, res) { + const { auth, body } = parseReq(req, { + body: z.object({ + experience: z.number(), + notifications: z.boolean() + }) + }); + + if (req.pid === null || !req.new_users || req.directory === 'web') { + return res.sendStatus(401); + } + + const user = await database.getUserSettings(auth().pid); + if (user) { + return res.sendStatus(504); // User already exists + } + + await createUser(auth().pid, body.experience, body.notifications); + if (await database.getUserSettings(auth().pid) !== null) { + res.sendStatus(200); + } else { + res.sendStatus(504); + } +}); diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/userpage.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/userpage.js index 0ebe4254..560f0050 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/userpage.js +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/userpage.js @@ -35,13 +35,21 @@ userPageRouter.get('/notifications.json', async function (req, res) { userPageRouter.get('/downloadUserData.json', async function (req, res) { res.set('Content-Type', 'text/json'); res.set('Content-Disposition', `attachment; filename="${req.pid}_user_data.json"`); - const posts = await POST.find({ pid: req.pid }); - const userContent = await database.getUserSettings(req.pid); - const userSettings = await database.getUserContent(req.pid); + const rawPosts = await POST.find({ pid: req.pid }); + const userSettings = await database.getUserSettings(req.pid); + const userContent = await database.getUserContent(req.pid); + + // Clean non-user data + userSettings.banned_by = null; + const postsJson = rawPosts.map(post => ({ + ...post.toJSON(), + removed_by: null + })); + const doc = { user_content: userContent, user_settings: userSettings, - posts: posts + posts: postsJson }; res.send(doc); }); diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/web.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/web.ts similarity index 80% rename from apps/juxtaposition-ui/src/services/juxt-web/routes/console/web.js rename to apps/juxtaposition-ui/src/services/juxt-web/routes/console/web.ts index 1ce1b5c7..55048200 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/console/web.js +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/console/web.ts @@ -11,31 +11,36 @@ webRouter.get('/', function (req, res) { }); webRouter.get('/css/:filename', function (req, res) { + const dir = req.directory ?? 'web'; res.set('Content-Type', 'text/css'); res.set('Cache-Control', `public, max-age=${maxAge}`); - res.sendFile('/css/' + req.params.filename, { root: path.join(distFolder, 'webfiles', req.directory) }); + res.sendFile('/css/' + req.params.filename, { root: path.join(distFolder, 'webfiles', dir) }); }); webRouter.get('/js/:filename', function (req, res) { + const dir = req.directory ?? 'web'; res.set('Content-Type', 'application/javascript; charset=utf-8'); res.set('Cache-Control', `public, max-age=${maxAge}`); - res.sendFile('/js/' + req.params.filename, { root: path.join(distFolder, 'webfiles', req.directory) }); + res.sendFile('/js/' + req.params.filename, { root: path.join(distFolder, 'webfiles', dir) }); }); webRouter.get('/images/:filename', function (req, res) { + const dir = req.directory ?? 'web'; res.set('Content-Type', 'image/png'); res.set('Cache-Control', `public, max-age=${maxAge}`); - res.sendFile('/images/' + req.params.filename, { root: path.join(distFolder, 'webfiles', req.directory) }); + res.sendFile('/images/' + req.params.filename, { root: path.join(distFolder, 'webfiles', dir) }); }); webRouter.get('/fonts/:filename', function (req, res) { + const dir = req.directory ?? 'web'; res.set('Content-Type', 'font/woff'); res.set('Cache-Control', `public, max-age=${maxAge}`); - res.sendFile('/fonts/' + req.params.filename, { root: path.join(distFolder, 'webfiles', req.directory) }); + res.sendFile('/fonts/' + req.params.filename, { root: path.join(distFolder, 'webfiles', dir) }); }); webRouter.get('/favicon.ico', function (req, res) { + const dir = req.directory ?? 'web'; res.set('Content-Type', 'image/x-icon'); res.set('Cache-Control', `public, max-age=${maxAge}`); - res.sendFile('/images/favicon.ico', { root: path.join(distFolder, 'webfiles', req.directory) }); + res.sendFile('/images/favicon.ico', { root: path.join(distFolder, 'webfiles', dir) }); }); diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/index.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/index.ts similarity index 100% rename from apps/juxtaposition-ui/src/services/juxt-web/routes/index.js rename to apps/juxtaposition-ui/src/services/juxt-web/routes/index.ts diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/routeUtils.ts b/apps/juxtaposition-ui/src/services/juxt-web/routes/routeUtils.ts index 20ba23c6..071165d3 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/routeUtils.ts +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/routeUtils.ts @@ -1,10 +1,10 @@ import type { GetUserDataResponse as AccountGetUserDataResponse } from '@pretendonetwork/grpc/account/get_user_data_rpc'; -import type { Request } from 'express'; +import type { Request, Response } from 'express'; import type { z } from 'zod'; import type { UserTokens } from '@/types/juxt/tokens'; import type { ParamPack } from '@/types/common/param-pack'; -type AnySchema = z.ZodObject | undefined | null; +type AnySchema = z.ZodObject | z.ZodPipe | undefined | null; export type AuthRequest = TReq & { user: AccountGetUserDataResponse; @@ -20,15 +20,20 @@ export type AuthContext = { paramPackData: null | ParamPack; }; -export type ParseRequestOptions = { +export type ParseRequestOptions = { body?: TBody; query?: TQuery; + params?: TParams; + files?: TFiles; }; -export type ParsedRequest = { +export type ParsedRequest = { body: TBody extends z.ZodType ? z.infer : undefined; query: TQuery extends z.ZodType ? z.infer : undefined; + params: TParams extends z.ZodType ? z.infer : undefined; + files: Record; auth: () => AuthContext; + hasAuth: () => boolean; }; export function getAuthedRequest(req: TReq): AuthRequest { @@ -38,9 +43,11 @@ export function getAuthedRequest(req: TReq): Aut return req as AuthRequest; } -export function parseReq(req: Request, ops?: ParseRequestOptions): ParsedRequest { +export function parseReq(req: Request, ops?: ParseRequestOptions): ParsedRequest { let body: any = undefined; let query: any = undefined; + let params: any = undefined; + const files = {} as Record; if (ops?.body) { const res = ops.body.safeParse(req.body); @@ -58,6 +65,21 @@ export function parseReq { + files[v as TFiles[number]] = reqFiles[v] ? (reqFiles[v] ?? []) : []; + }); + } + function getAuthContext(): AuthContext { const authedReq = getAuthedRequest(req); const result: AuthContext = { @@ -69,9 +91,29 @@ export function parseReq; + params, + files, + auth: getAuthContext, + hasAuth + } as ParsedRequest; +} + +/** + * Returns a Mongoose filter if the account isn't a moderator. + */ +export function ifNotMod(res: Response, filter: T): T | {} { + if (res.locals.moderator) { + return {}; + } + return filter; } diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/web/login.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/web/login.tsx similarity index 56% rename from apps/juxtaposition-ui/src/services/juxt-web/routes/web/login.js rename to apps/juxtaposition-ui/src/services/juxt-web/routes/web/login.tsx index 8777bef1..2da2fcc7 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/routes/web/login.js +++ b/apps/juxtaposition-ui/src/services/juxt-web/routes/web/login.tsx @@ -1,29 +1,45 @@ import express from 'express'; +import { z } from 'zod'; import { database } from '@/database'; import { passwordLogin, getUserDataFromToken } from '@/util'; import { config } from '@/config'; import { logger } from '@/logger'; +import { WebLoginView } from '@/services/juxt-web/views/web/loginView'; +import { buildContext } from '@/services/juxt-web/views/context'; +import { parseReq } from '@/services/juxt-web/routes/routeUtils'; export const loginRouter = express.Router(); const cookieDomain = config.http.cookieDomain; loginRouter.get('/', async function (req, res) { - res.render(req.directory + '/login.ejs', { toast: null, redirect: req.query.redirect ?? '/' }); + const { query } = parseReq(req, { + query: z.object({ + redirect: z.string().optional() + }) + }); + return res.jsx(); }); loginRouter.post('/', async (req, res) => { - const { username, password, redirect } = req.body; + const { body } = parseReq(req, { + body: z.object({ + username: z.string(), + password: z.string(), + redirect: z.string().default('/') + }) + }); + const { username, password, redirect } = body; const login = await passwordLogin(username, password).catch((e) => { switch (e.details) { case 'INVALID_ARGUMENT: User not found': - res.render(req.directory + '/login.ejs', { toast: 'Username was invalid.', redirect }); + res.jsx(); break; case 'INVALID_ARGUMENT: Password is incorrect': - res.render(req.directory + '/login.ejs', { toast: 'Password was incorrect.', redirect }); + res.jsx(); break; default: logger.error(e, `Login error for ${username}`); - res.render(req.directory + '/login.ejs', { toast: 'Invalid username or password.', redirect }); + res.jsx(); break; } }); @@ -33,17 +49,14 @@ loginRouter.post('/', async (req, res) => { const PNID = await getUserDataFromToken(login.accessToken); if (!PNID) { - return res.render(req.directory + '/login.ejs', { toast: 'Invalid username or password.', redirect }); + return res.jsx(); } - let discovery = await database.getEndPoint(config.serverEnvironment); - if (!discovery) { - discovery = { - status: 5 - }; - } + const discovery = await database.getEndPoint(config.serverEnvironment); + const discoveryStatus = discovery?.status ?? 5; + let message = ''; - switch (discovery.status) { + switch (discoveryStatus) { case 3: message = 'Juxt is currently undergoing maintenance. Please try again later.'; break; @@ -54,10 +67,10 @@ loginRouter.post('/', async (req, res) => { message = 'Juxt is currently unavailable. Please try again later.'; break; } - if (discovery.status !== 0) { - return res.render(req.directory + '/error.ejs', { + if (discoveryStatus !== 0) { + return res.renderError({ code: 504, - message: message + message }); } const expiration = login.expiresIn * 60 * 60; diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/web/pwa.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/web/pwa.ts similarity index 100% rename from apps/juxtaposition-ui/src/services/juxt-web/routes/web/pwa.js rename to apps/juxtaposition-ui/src/services/juxt-web/routes/web/pwa.ts diff --git a/apps/juxtaposition-ui/src/services/juxt-web/routes/web/robots.js b/apps/juxtaposition-ui/src/services/juxt-web/routes/web/robots.ts similarity index 100% rename from apps/juxtaposition-ui/src/services/juxt-web/routes/web/robots.js rename to apps/juxtaposition-ui/src/services/juxt-web/routes/web/robots.ts diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/context.ts b/apps/juxtaposition-ui/src/services/juxt-web/views/context.ts index 10b53d17..7b1c0de1 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/context.ts +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/context.ts @@ -6,6 +6,7 @@ export type RenderContext = { lang: Record; cdnUrl: string; moderator: boolean; + developer: boolean; pid: number; uaIsConsole?: boolean; // user agent looks like a Nintendo console usersMap: HashMap; // map of PID -> screen name @@ -25,6 +26,7 @@ export function buildContext(res: Response): RenderContext { lang: locals.lang, cdnUrl: locals.cdnURL, moderator: locals.moderator, + developer: locals.developer, uaIsConsole: locals.uaIsConsole, pid: locals.pid }; diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityListView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityListView.tsx new file mode 100644 index 00000000..63990f0f --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityListView.tsx @@ -0,0 +1,85 @@ +import { utils } from '@/services/juxt-web/views/utils'; +import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root'; +import type { ReactNode } from 'react'; +import type { CommunityItemProps, CommunityListViewProps, CommunityOverviewViewProps } from '@/services/juxt-web/views/web/communityListView'; + +export function CtrCommunityItem(props: CommunityItemProps): ReactNode { + const id = props.community.olive_community_id; + return ( +
  • + + +
    +
    + {props.community.name} +
    + + {props.community.followers} + {' '} + {props.ctx.lang.community.followers} + +
    +
    +
    +
  • + ); +} + +export function CtrCommunityListView(props: CommunityListViewProps): ReactNode { + return ( + + + +
    +
    +
      + {props.communities.map(community => ( + + ))} +
    +
    +
    +
    +
    + ); +} + +export function CtrCommunityOverviewView(props: CommunityOverviewViewProps): ReactNode { + return ( + + + +
    +
    +
    +

    {props.ctx.lang.all_communities.popular_places}

    +
    +
      + {props.popularCommunities.map(community => ( + + ))} +
    +
    +

    {props.ctx.lang.all_communities.new_communities}

    +
    +
      + {props.newCommunities.map(community => ( + + ))} +
    +
    +
    +
    +
    + ); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityView.tsx new file mode 100644 index 00000000..c82c7e30 --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/communityView.tsx @@ -0,0 +1,111 @@ +import cx from 'classnames'; +import { utils } from '@/services/juxt-web/views/utils'; +import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root'; +import { CtrNewPostView } from '@/services/juxt-web/views/ctr/newPostView'; +import { CtrPostListClosedView } from '@/services/juxt-web/views/ctr/postList'; +import type { ReactNode } from 'react'; +import type { CommunityViewProps } from '@/services/juxt-web/views/web/communityView'; + +export function CtrCommunityView(props: CommunityViewProps): ReactNode { + const community = props.community; + const imageId = community.parent ? community.parent : community.olive_community_id; + const bannerUrl = community.ctr_header + ? utils.cdn(props.ctx, community.ctr_header) + : utils.cdn(props.ctx, `/headers/${imageId}/3DS.png`); + + return ( + + + +
    +
    +
    + +
  • + {props.ctx.lang.community.recent} +
  • +
  • + {props.ctx.lang.community.popular} +
  • +
    +
    + {!community.permissions.open ? : null} + {props.children} +
    +
    + +
    +
    + ); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/errorView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/errorView.tsx new file mode 100644 index 00000000..cb072320 --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/errorView.tsx @@ -0,0 +1,57 @@ +import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root'; +import { InlineScript } from '@/services/juxt-web/views/common'; +import type { ReactNode } from 'react'; +import type { ErrorViewProps, FatalErrorViewProps } from '@/services/juxt-web/views/web/errorView'; + +export function CtrErrorView(props: ErrorViewProps): ReactNode { + const title = `Error: ${props.code}`; + + return ( + + + +
    +

    Whoops! Looks like we couldn't find the page you're looking for.

    +

    Double-check your link or try again later

    +
    +
    +
    + ); +} + +const errorJs = ` +var e = document.getElementById('error'); +var code = parseInt(e.getAttribute('data-code')); +var message = e.getAttribute('data-message'); + +cave.error_callFreeErrorViewer(code, message); +cave.closeApplication(); +`; + +export function CtrFatalErrorView(props: FatalErrorViewProps): ReactNode { + return ( + + + + + +

    + You are not authorized to access this application ( + {props.code} + ) +

    +

    {props.message}

    + + + + ); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/firstRunView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/firstRunView.tsx new file mode 100644 index 00000000..126ed3ad --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/firstRunView.tsx @@ -0,0 +1,255 @@ +import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root'; +import type { ReactNode } from 'react'; +import type { FirstRunViewProps } from '@/services/juxt-web/views/web/firstRunView'; + +const AboutSection = { + Root(props: { children?: ReactNode; id?: string; visible?: boolean }): ReactNode { + return ( +
    + {props.children} +
    + ); + }, + Title(props: { children?: ReactNode }): ReactNode { + return ( +

    {props.children}

    + ); + }, + Body(props: { children?: ReactNode }): ReactNode { + return ( +
    + {props.children} +
    + ); + }, + BackButton(props: { currentId: string; onClick?: string; previousId?: string; text: string }): ReactNode { + return ( + + ); + }, + NextButton(props: { currentId: string; onClick?: string; nextId?: string; text: string; sound?: string }): ReactNode { + return ( + + ); + } +}; + +export function CtrFirstRunView(props: FirstRunViewProps): ReactNode { + const sections = { + welcome: 'welcome', + beta: 'beta', + about: 'about', + manners: 'manners', + ga: 'google-analytics', + experience: 'game-experience', + ready: 'ready', + end: 'have-fun' + }; + + const head = ( + <> + + + + ); + + return ( + + + +
    +
    + + + {props.ctx.lang.setup.welcome} + +

    + {props.ctx.lang.setup.welcome_text} +

    +
    + + + +
    + + + {props.ctx.lang.setup.beta} + +

    + {props.ctx.lang.setup.beta_text.first} +
    + {props.ctx.lang.setup.beta_text.second} +
    + {props.ctx.lang.setup.beta_text.third} +

    +
    + + + +
    + + + {props.ctx.lang.setup.info} + +

    + {props.ctx.lang.setup.info_text} +

    +
    + + + +
    + + + {props.ctx.lang.setup.rules} + +

    + { props.ctx.lang.setup.rules_text.first } +
    +
    +

    +

    { props.ctx.lang.setup.rules_text.second }

    +
    + { props.ctx.lang.setup.rules_text.third } +
    +
    +
    +

    { props.ctx.lang.setup.rules_text.fourth }

    +
    + { props.ctx.lang.setup.rules_text.fifth } +
    +
    +
    +

    { props.ctx.lang.setup.rules_text.sixth }

    +
    + { props.ctx.lang.setup.rules_text.seventh } +
    +
    +
    +

    { props.ctx.lang.setup.rules_text.eighth }

    +
    + { props.ctx.lang.setup.rules_text.ninth } +
    +
    +
    +

    { props.ctx.lang.setup.rules_text.tenth }

    +
    + { props.ctx.lang.setup.rules_text.eleventh } +
    +
    +
    +

    { props.ctx.lang.setup.rules_text.twelfth }

    +
    + { props.ctx.lang.setup.rules_text.thirteenth } +
    +
    +

    +
    + + + +
    + + + {props.ctx.lang.setup.google} + +

    + { props.ctx.lang.setup.google_text } +

    +
    + + + +
    + + + {props.ctx.lang.setup.experience} + +

    + { props.ctx.lang.setup.experience_text.info } +

    +
    + + + + +
    + + + {props.ctx.lang.setup.ready} + +

    + { props.ctx.lang.setup.ready_text } +

    +
    + + + +
    + + + + + {props.ctx.lang.setup.done} + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + ); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messageThread.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messageThread.tsx index 8f8ec9ec..569b8f28 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messageThread.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messageThread.tsx @@ -51,7 +51,7 @@ export function CtrMessageThreadView(props: MessageThreadViewProps): ReactNode { const otherUserName = props.ctx.usersMap.get(props.otherUser.pid) ?? ''; return ( - + diff --git a/apps/juxtaposition-ui/webfiles/portal/css/firstrun.css b/apps/juxtaposition-ui/webfiles/portal/css/firstrun.css new file mode 100644 index 00000000..9b8fb860 --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/portal/css/firstrun.css @@ -0,0 +1,83 @@ +.background { + background: #fff; + border-radius: 20px; + box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6); + margin: 40px auto; + width: 890px; + padding: 25px 75px; + display: none; +} +.fixed-bottom-button { + padding: 0 !important; + width: 185px !important; +} +.about-wrapper.background { + min-height: 500px; +} +.about-button { + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45); + height: 60px; + padding: 0 25px; + border-radius: 12px; + border: none; + font-size: 20px; + color: white; + font-weight: bold; + margin: 0 auto; + display: block; + background: -webkit-gradient(linear, left top, left bottom, from(#866dbe), to(#673DB6)) 0 0; +} +.about-header { + font-weight: bold; + text-align: center; + color: #866dbe; + font-size: 30px; + margin: 15px 0; +} +h3 { + font-weight: bold; + text-align: center; + color: #44cca1; + font-size: 25px; + margin: 15px 0; +} +ul.horizontal-list { + color: white; + background: -webkit-gradient(linear, left top, left bottom, from(#6045af), to(#634ca8)) 0 0 !important; + display: block; + border-radius: 50px; + padding: 15px 15px; +} + +ul.horizontal-list li { + width: 287px; + height: 50px; + cursor: pointer; + background-color: #5e44aa; + box-shadow: inset 0 7px 10px -3px rgba(66, 45, 120, 0.75), inset 0 -3px 5px 0 rgba(128, 101, 213, 0.5); + display: table-cell; + text-align: center; +} + +ul.horizontal-list li:first-child { + border-radius: 50px 0 0 50px; +} + +ul.horizontal-list li:last-child { + border-radius: 0 50px 50px 0; +} + +ul.horizontal-list li > a { + text-shadow: none; + border-radius: 50px; + text-align: center; + line-height: 60px; + display: inline-block; + width: 287px; + height: 60px; + color: #fff; +} +ul.horizontal-list li.selected > a { + background: -webkit-gradient(linear, left top, left bottom, from(#fff), color-stop(0.3, #fff), color-stop(0.8, #fff5f5), color-stop(0.96, #fff5f5), to(#fff5f5)) 0 0; + color: #4d4d4d; +} \ No newline at end of file diff --git a/apps/juxtaposition-ui/src/webfiles/portal/css/juxt.css b/apps/juxtaposition-ui/webfiles/portal/css/juxt.css similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/css/juxt.css rename to apps/juxtaposition-ui/webfiles/portal/css/juxt.css diff --git a/apps/juxtaposition-ui/src/webfiles/portal/fonts/Poppins-Light.eot b/apps/juxtaposition-ui/webfiles/portal/fonts/Poppins-Light.eot similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/fonts/Poppins-Light.eot rename to apps/juxtaposition-ui/webfiles/portal/fonts/Poppins-Light.eot diff --git a/apps/juxtaposition-ui/src/webfiles/portal/fonts/Poppins-Light.otf b/apps/juxtaposition-ui/webfiles/portal/fonts/Poppins-Light.otf similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/fonts/Poppins-Light.otf rename to apps/juxtaposition-ui/webfiles/portal/fonts/Poppins-Light.otf diff --git a/apps/juxtaposition-ui/src/webfiles/portal/fonts/Poppins-Light.svg b/apps/juxtaposition-ui/webfiles/portal/fonts/Poppins-Light.svg similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/fonts/Poppins-Light.svg rename to apps/juxtaposition-ui/webfiles/portal/fonts/Poppins-Light.svg diff --git a/apps/juxtaposition-ui/src/webfiles/portal/fonts/Poppins-Light.ttf b/apps/juxtaposition-ui/webfiles/portal/fonts/Poppins-Light.ttf similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/fonts/Poppins-Light.ttf rename to apps/juxtaposition-ui/webfiles/portal/fonts/Poppins-Light.ttf diff --git a/apps/juxtaposition-ui/src/webfiles/portal/fonts/Poppins-Light.woff b/apps/juxtaposition-ui/webfiles/portal/fonts/Poppins-Light.woff similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/fonts/Poppins-Light.woff rename to apps/juxtaposition-ui/webfiles/portal/fonts/Poppins-Light.woff diff --git a/apps/juxtaposition-ui/src/webfiles/portal/images/add-post-no-image.png b/apps/juxtaposition-ui/webfiles/portal/images/add-post-no-image.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/images/add-post-no-image.png rename to apps/juxtaposition-ui/webfiles/portal/images/add-post-no-image.png diff --git a/apps/juxtaposition-ui/src/webfiles/portal/images/background.png b/apps/juxtaposition-ui/webfiles/portal/images/background.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/images/background.png rename to apps/juxtaposition-ui/webfiles/portal/images/background.png diff --git a/apps/juxtaposition-ui/src/webfiles/portal/images/bandwidthalert.png b/apps/juxtaposition-ui/webfiles/portal/images/bandwidthalert.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/images/bandwidthalert.png rename to apps/juxtaposition-ui/webfiles/portal/images/bandwidthalert.png diff --git a/apps/juxtaposition-ui/src/webfiles/portal/images/bandwidthlost.png b/apps/juxtaposition-ui/webfiles/portal/images/bandwidthlost.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/images/bandwidthlost.png rename to apps/juxtaposition-ui/webfiles/portal/images/bandwidthlost.png diff --git a/apps/juxtaposition-ui/src/webfiles/portal/images/banner.png b/apps/juxtaposition-ui/webfiles/portal/images/banner.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/images/banner.png rename to apps/juxtaposition-ui/webfiles/portal/images/banner.png diff --git a/apps/juxtaposition-ui/src/webfiles/portal/images/favicon.ico b/apps/juxtaposition-ui/webfiles/portal/images/favicon.ico similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/images/favicon.ico rename to apps/juxtaposition-ui/webfiles/portal/images/favicon.ico diff --git a/apps/juxtaposition-ui/src/webfiles/portal/images/splash-background.png b/apps/juxtaposition-ui/webfiles/portal/images/splash-background.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/images/splash-background.png rename to apps/juxtaposition-ui/webfiles/portal/images/splash-background.png diff --git a/apps/juxtaposition-ui/webfiles/portal/js/api.ts b/apps/juxtaposition-ui/webfiles/portal/js/api.ts new file mode 100644 index 00000000..598cb17b --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/portal/js/api.ts @@ -0,0 +1,54 @@ +import { POST, DELETE } from './xhr'; + +export type EmpathyPostResponse = { + status: number; + id: string; + count: number; +}; + +export function empathyPostById(id: string, cb: (post: EmpathyPostResponse) => void): void { + var params = 'postID=' + id; + + POST('/posts/empathy', params, (xhr) => { + if (xhr.status !== 200) { + return cb({ status: xhr.status, id, count: 0 }); + } + var post: EmpathyPostResponse; + + try { + post = JSON.parse(xhr.responseText); + } catch (e) { + console.error(e); + return cb({ status: 400, id, count: 0 }); + } + + cb(post); + }); +} + +export type DeletePostResponse = { + status: number; + nextUrl: string; +}; + +export function deletePostById(id: string, reason: string | null, cb: (result: DeletePostResponse) => void): void { + var postUrl = `/posts/${id}`; + var query = reason !== null ? `?reason=${encodeURIComponent(reason)}` : ''; + + DELETE(postUrl + query, function (xhr) { + if (xhr.status !== 200) { + return cb({ status: xhr.status, nextUrl: postUrl }); + } + + // HACK: we haven't actually formalised this API serverside yet + // for now, synthesise the response object ourselves + // make sure it looks like a URL... + var nextUrl = xhr.responseText; + if (!/^\/[\w/]+$/.test(nextUrl)) { + return cb({ status: 400, nextUrl: postUrl }); + } + + // make a response object + return cb({ status: 200, nextUrl }); + }); +} diff --git a/apps/juxtaposition-ui/src/webfiles/portal/js/debug.js b/apps/juxtaposition-ui/webfiles/portal/js/debug.js similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/js/debug.js rename to apps/juxtaposition-ui/webfiles/portal/js/debug.js diff --git a/apps/juxtaposition-ui/webfiles/portal/js/firstrun.js b/apps/juxtaposition-ui/webfiles/portal/js/firstrun.js new file mode 100644 index 00000000..7496a7bc --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/portal/js/firstrun.js @@ -0,0 +1,40 @@ +var experience = 0; +var notifications = false; + +function selectExperience(type) { + document.getElementById('beginner').classList.remove('selected'); + document.getElementById('intermediate').classList.remove('selected'); + document.getElementById('expert').classList.remove('selected'); + switch (type) { + case 0: + document.getElementById('beginner').classList.add('selected'); + experience = 0; + break; + case 1: + document.getElementById('intermediate').classList.add('selected'); + experience = 1; + break; + case 2: + document.getElementById('expert').classList.add('selected'); + experience = 2; + break; + } + wiiuSound.playSoundByName('SE_OLV_OK', 1); +} +window.selectExperience = selectExperience; + +function firstRunSubmit() { + var xmlhttp = new XMLHttpRequest(); // new HttpRequest instance + var theUrl = '/titles/show/newUser'; + xmlhttp.open('POST', theUrl); + xmlhttp.onreadystatechange = function () { + if (this.readyState === 4 && (this.status === 423 || this.status === 404 || this.status === 504)) { + wiiuErrorViewer.openByCodeAndMessage(5986000 + this.status, 'Unable to complete setup, the server is likely having issues.\n\nPlease try again later.'); + wiiuBrowser.closeApplication(); + } + }; + xmlhttp.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); + xmlhttp.send(JSON.stringify({ experience: experience, notifications: notifications })); + wiiuSound.playSoundByName('JGL_OLV_INIT_END', 3); +} +window.firstRunSubmit = firstRunSubmit; diff --git a/apps/juxtaposition-ui/src/webfiles/portal/js/juxt.js b/apps/juxtaposition-ui/webfiles/portal/js/juxt.js similarity index 87% rename from apps/juxtaposition-ui/src/webfiles/portal/js/juxt.js rename to apps/juxtaposition-ui/webfiles/portal/js/juxt.js index cc0c30ab..93fd3b10 100644 --- a/apps/juxtaposition-ui/src/webfiles/portal/js/juxt.js +++ b/apps/juxtaposition-ui/webfiles/portal/js/juxt.js @@ -1,4 +1,7 @@ import Pjax from 'pjax'; +import { GET, POST } from './xhr'; +import { empathyPostById } from './api'; +import { initPostPageView } from './post'; var pjax; setInterval(checkForUpdates, 30000); @@ -36,7 +39,6 @@ function initYeah() { var parent = document.getElementById(id); var count = document.getElementById('count-' + id); el.disabled = true; - var params = 'postID=' + id; if (el.classList.contains('selected')) { el.classList.remove('selected'); parent.classList.remove('yeah'); @@ -53,9 +55,8 @@ function initYeah() { wiiuSound.playSoundByName('SE_WAVE_MII_ADD', 1); } - POST('/posts/empathy', params, function a(data) { - var post = JSON.parse(data.response); - if (!post || post.status !== 200) { + empathyPostById(id, function (post) { + if (post.status !== 200) { // Apparently there was an actual error code for not being able to yeah a post, who knew! // TODO: Find more of these return wiiuErrorViewer.openByCode(1155927); @@ -225,6 +226,7 @@ function initAll() { initPosts(); initMorePosts(); initPostModules(); + initPostPageView(); initSounds(); checkForUpdates(); pjax.refresh(); @@ -358,25 +360,6 @@ function exit() { } window.exit = exit; -function deletePost(post) { - var id = post.getAttribute('data-post'); - if (!id) { - return; - } - var confirm = wiiuDialog.confirm('Are you sure you want to delete your post? This cannot be undone.', 'No', 'Yes'); - if (confirm) { - DELETE('/posts/' + id, function a(data) { - if (!data || data.status !== 200) { - return wiiuErrorViewer.openByCodeAndMessage('5980030', 'Post was not able to be deleted. Please try again later.'); - } - console.log(data); - alert('Post has been deleted.'); - return window.location.href = data.responseText; - }); - } -} -window.deletePost = deletePost; - function reportPost(post) { var id = post.getAttribute('data-post'); var button = document.getElementById('report-launcher'); @@ -422,44 +405,6 @@ function checkForUpdates() { } }); } -function POST(url, data, callback) { - wiiuBrowser.showLoadingIcon(true); - var xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function () { - if (this.readyState === 4) { - wiiuBrowser.showLoadingIcon(false); - return callback(this); - } - }; - xhttp.open('POST', url, true); - xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - xhttp.send(data); -} -function GET(url, callback) { - wiiuBrowser.showLoadingIcon(true); - var xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function () { - if (this.readyState === 4) { - wiiuBrowser.showLoadingIcon(false); - return callback(this); - } - }; - xhttp.open('GET', url, true); - xhttp.send(); -} - -function DELETE(url, callback) { - wiiuBrowser.showLoadingIcon(true); - var xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function () { - if (this.readyState === 4) { - wiiuBrowser.showLoadingIcon(false); - return callback(this); - } - }; - xhttp.open('DELETE', url, true); - xhttp.send(); -} function back() { if (wiiuBrowser.canHistoryBack()) { diff --git a/apps/juxtaposition-ui/webfiles/portal/js/post.ts b/apps/juxtaposition-ui/webfiles/portal/js/post.ts new file mode 100644 index 00000000..0c5201b0 --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/portal/js/post.ts @@ -0,0 +1,41 @@ +// Script for the post page view (post.ejs/postPageView.tsx) +import { deletePostById } from './api'; + +function deletePost(this: HTMLElement, _e: Event): void { + var id = this.getAttribute('data-button-delete-post'); + if (!id) { + return; + } + + var confirm = wiiuDialog.confirm( + 'Are you sure you want to delete your post? This cannot be undone.', + 'No', + 'Yes' + ); + if (!confirm) { + return; + } + + deletePostById(id, null, function (response) { + if (response.status !== 200) { + return wiiuErrorViewer.openByCodeAndMessage( + 5980030, + 'Post was not able to be deleted. Please try again later.' + ); + } + + alert('Post has been deleted.'); + window.location.href = response.nextUrl; + }); +} + +function initDeleteButton(): void { + var buttons = document.querySelectorAll('[data-button-delete-post]'); + for (var i = 0; i < buttons.length; i++) { + (buttons[i] as HTMLElement).addEventListener('click', deletePost); + } +} + +export function initPostPageView(): void { + initDeleteButton(); +} diff --git a/apps/juxtaposition-ui/webfiles/portal/js/xhr.ts b/apps/juxtaposition-ui/webfiles/portal/js/xhr.ts new file mode 100644 index 00000000..22c1d6f1 --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/portal/js/xhr.ts @@ -0,0 +1,33 @@ +export type XHRCallback = (request: XMLHttpRequest) => void; + +export function POST(url: string, data: string, callback: XHRCallback): void { + wiiuBrowser.showLoadingIcon(true); + var xhttp = new XMLHttpRequest(); + xhttp.onload = function (): void { + wiiuBrowser.showLoadingIcon(false); + callback(this); + }; + xhttp.open('POST', url, true); + xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + xhttp.send(data); +} + +export function GET(url: string, callback: XHRCallback): void { + var xhttp = new XMLHttpRequest(); + xhttp.onload = function (): void { + callback(this); + }; + xhttp.open('GET', url, true); + xhttp.send(); +} + +export function DELETE(url: string, callback: XHRCallback): void { + wiiuBrowser.showLoadingIcon(true); + var xhttp = new XMLHttpRequest(); + xhttp.onload = function (): void { + wiiuBrowser.showLoadingIcon(false); + callback(this); + }; + xhttp.open('DELETE', url, true); + xhttp.send(); +} diff --git a/apps/juxtaposition-ui/src/webfiles/portal/notifications.ejs b/apps/juxtaposition-ui/webfiles/portal/notifications.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/notifications.ejs rename to apps/juxtaposition-ui/webfiles/portal/notifications.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/portal/partials/following_list.ejs b/apps/juxtaposition-ui/webfiles/portal/partials/following_list.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/partials/following_list.ejs rename to apps/juxtaposition-ui/webfiles/portal/partials/following_list.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/portal/partials/head.ejs b/apps/juxtaposition-ui/webfiles/portal/partials/head.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/partials/head.ejs rename to apps/juxtaposition-ui/webfiles/portal/partials/head.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/portal/partials/nav_bar.ejs b/apps/juxtaposition-ui/webfiles/portal/partials/nav_bar.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/partials/nav_bar.ejs rename to apps/juxtaposition-ui/webfiles/portal/partials/nav_bar.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/portal/partials/notifications.ejs b/apps/juxtaposition-ui/webfiles/portal/partials/notifications.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/partials/notifications.ejs rename to apps/juxtaposition-ui/webfiles/portal/partials/notifications.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/portal/partials/post_template.ejs b/apps/juxtaposition-ui/webfiles/portal/partials/post_template.ejs similarity index 98% rename from apps/juxtaposition-ui/src/webfiles/portal/partials/post_template.ejs rename to apps/juxtaposition-ui/webfiles/portal/partials/post_template.ejs index d910e019..8d103824 100644 --- a/apps/juxtaposition-ui/src/webfiles/portal/partials/post_template.ejs +++ b/apps/juxtaposition-ui/webfiles/portal/partials/post_template.ejs @@ -63,7 +63,7 @@ <%}%> <% if(reply && post.pid === pid || reply && moderator) {%>
    - +
    <%}%> <%if(!mainPost) {%> diff --git a/apps/juxtaposition-ui/src/webfiles/portal/partials/posts_list.ejs b/apps/juxtaposition-ui/webfiles/portal/partials/posts_list.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/partials/posts_list.ejs rename to apps/juxtaposition-ui/webfiles/portal/partials/posts_list.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/portal/partials/requests.ejs b/apps/juxtaposition-ui/webfiles/portal/partials/requests.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/partials/requests.ejs rename to apps/juxtaposition-ui/webfiles/portal/partials/requests.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/portal/settings.ejs b/apps/juxtaposition-ui/webfiles/portal/settings.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/settings.ejs rename to apps/juxtaposition-ui/webfiles/portal/settings.ejs diff --git a/apps/juxtaposition-ui/webfiles/portal/tsconfig.json b/apps/juxtaposition-ui/webfiles/portal/tsconfig.json new file mode 100644 index 00000000..027b5c3b --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/portal/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "strict": true, + "sourceMap": true, + "resolveJsonModule": true, + "module": "ES2022", + "esModuleInterop": true, + "moduleResolution": "bundler", + "skipLibCheck": true, + "baseUrl": ".", + "outDir": "dist", + "allowJs": true, + "target": "es2022", + "noEmitOnError": true, + "noImplicitAny": true, + "strictPropertyInitialization": true, + "verbatimModuleSyntax": true, + "types": [ + "@pretendonetwork/wiiu-browser-types", + "@pretendonetwork/wiiu-dialog-types", + "@pretendonetwork/wiiu-error-viewer-types" + ], + "paths": { + "@/*": [ + "./*" + ] + } + }, + "include": [ + "js" + ] +} \ No newline at end of file diff --git a/apps/juxtaposition-ui/src/webfiles/portal/user_page.ejs b/apps/juxtaposition-ui/webfiles/portal/user_page.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/portal/user_page.ejs rename to apps/juxtaposition-ui/webfiles/portal/user_page.ejs diff --git a/apps/juxtaposition-ui/webfiles/web/css/admin.css b/apps/juxtaposition-ui/webfiles/web/css/admin.css new file mode 100644 index 00000000..27eec403 --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/css/admin.css @@ -0,0 +1,42 @@ +.community-create { + .col-md-4, .col-md-3, .col-md-9 { + display: flex; + align-items: center; + padding: 0.5em 0; + justify-content: center; + } + input[type="text"], input[type="file"], select, textarea { + width: 200px; + margin-left: 0.5em; + background: var(--text-secondary); + border: none; + border-radius: 5px; + height: 25px; + } + textarea { + height: 100px; + } + .labels { + width: 300px; + text-align: right; + } + input[type="file"] { + width: unset; + background: unset; + } + #browserIconPreview, #CTRbrowserHeaderPreview, #WiiUbrowserHeaderPreview { + object-fit: cover; + } + #browserIconPreview { + width: 128px; + height: 128px; + } + #CTRbrowserHeaderPreview { + width: 200px; + height: 110px; + } + #WiiUbrowserHeaderPreview { + width: 320px; + height: 45px; + } +} \ No newline at end of file diff --git a/apps/juxtaposition-ui/src/webfiles/web/css/first_run.css b/apps/juxtaposition-ui/webfiles/web/css/first_run.css similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/css/first_run.css rename to apps/juxtaposition-ui/webfiles/web/css/first_run.css diff --git a/apps/juxtaposition-ui/src/webfiles/web/css/login.css b/apps/juxtaposition-ui/webfiles/web/css/login.css similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/css/login.css rename to apps/juxtaposition-ui/webfiles/web/css/login.css diff --git a/apps/juxtaposition-ui/src/webfiles/web/css/web.css b/apps/juxtaposition-ui/webfiles/web/css/web.css similarity index 99% rename from apps/juxtaposition-ui/src/webfiles/web/css/web.css rename to apps/juxtaposition-ui/webfiles/web/css/web.css index 35382de2..4a7ded93 100644 --- a/apps/juxtaposition-ui/src/webfiles/web/css/web.css +++ b/apps/juxtaposition-ui/webfiles/web/css/web.css @@ -284,7 +284,7 @@ header > .selected > svg { } /* Red fill for .selected icons (Yeah'd post) */ -.post-buttons-wrapper > span.selected > svg { +.post-buttons-wrapper > span.selected svg { fill: red; stroke: red; } @@ -350,7 +350,7 @@ iframe { color: var(--text); text-align: center; position: fixed; - z-index: 10; + z-index: 1000; left: 50%; top: 100vh; opacity: 0; @@ -1336,16 +1336,15 @@ li.reports .button-spacer { text-wrap: nowrap; } -#search { +.searchbar { margin-top: 16px; width: 20em; height: 2em; border-radius: 1em; - padding: 2ch; border: 1px solid var(--background-alt-alt); font-size: 1rem; background-color: var(--background-alt); - padding: 12px; + padding: 2px 12px; color: var(--text); } @@ -1627,7 +1626,6 @@ button.report { .buttons.tabs>a, .buttons.tabs>button { - width: 100%; margin: 0 1ch; border: 1px solid var(--text-secondary); cursor: pointer; diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/add-post-no-image.png b/apps/juxtaposition-ui/webfiles/web/images/add-post-no-image.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/add-post-no-image.png rename to apps/juxtaposition-ui/webfiles/web/images/add-post-no-image.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/bandwidthalert.png b/apps/juxtaposition-ui/webfiles/web/images/bandwidthalert.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/bandwidthalert.png rename to apps/juxtaposition-ui/webfiles/web/images/bandwidthalert.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/bandwidthlost.png b/apps/juxtaposition-ui/webfiles/web/images/bandwidthlost.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/bandwidthlost.png rename to apps/juxtaposition-ui/webfiles/web/images/bandwidthlost.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/banner.png b/apps/juxtaposition-ui/webfiles/web/images/banner.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/banner.png rename to apps/juxtaposition-ui/webfiles/web/images/banner.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/favicon.ico b/apps/juxtaposition-ui/webfiles/web/images/favicon.ico similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/favicon.ico rename to apps/juxtaposition-ui/webfiles/web/images/favicon.ico diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-128x128.png b/apps/juxtaposition-ui/webfiles/web/images/icons/icon-128x128.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-128x128.png rename to apps/juxtaposition-ui/webfiles/web/images/icons/icon-128x128.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-144x144.png b/apps/juxtaposition-ui/webfiles/web/images/icons/icon-144x144.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-144x144.png rename to apps/juxtaposition-ui/webfiles/web/images/icons/icon-144x144.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-152x152.png b/apps/juxtaposition-ui/webfiles/web/images/icons/icon-152x152.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-152x152.png rename to apps/juxtaposition-ui/webfiles/web/images/icons/icon-152x152.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-192x192.png b/apps/juxtaposition-ui/webfiles/web/images/icons/icon-192x192.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-192x192.png rename to apps/juxtaposition-ui/webfiles/web/images/icons/icon-192x192.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-384x384.png b/apps/juxtaposition-ui/webfiles/web/images/icons/icon-384x384.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-384x384.png rename to apps/juxtaposition-ui/webfiles/web/images/icons/icon-384x384.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-512x512.png b/apps/juxtaposition-ui/webfiles/web/images/icons/icon-512x512.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-512x512.png rename to apps/juxtaposition-ui/webfiles/web/images/icons/icon-512x512.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-72x72.png b/apps/juxtaposition-ui/webfiles/web/images/icons/icon-72x72.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-72x72.png rename to apps/juxtaposition-ui/webfiles/web/images/icons/icon-72x72.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-96x96.png b/apps/juxtaposition-ui/webfiles/web/images/icons/icon-96x96.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/icons/icon-96x96.png rename to apps/juxtaposition-ui/webfiles/web/images/icons/icon-96x96.png diff --git a/apps/juxtaposition-ui/src/webfiles/web/images/splash-background.png b/apps/juxtaposition-ui/webfiles/web/images/splash-background.png similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/images/splash-background.png rename to apps/juxtaposition-ui/webfiles/web/images/splash-background.png diff --git a/apps/juxtaposition-ui/webfiles/web/js/admin.js b/apps/juxtaposition-ui/webfiles/web/js/admin.js new file mode 100644 index 00000000..2b47b5cb --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/js/admin.js @@ -0,0 +1,139 @@ +import { DateTime } from 'luxon'; + +function initBanLiftDate() { + // input type datetime-local + const picker = document.getElementById('ban_lift_date_picker'); + if (!picker) { + return; // No date picker to init + } + + // input type hidden + const submission = document.getElementById('ban_lift_date'); + // span + const utcDisplay = document.getElementById('ban_lift_date_utc'); + // span + const durationDisplay = document.getElementById('ban_lift_date_duration'); + + function updateElements(datetime) { + picker.value = datetime.toLocal().toISO({ includeOffset: false }); + submission.value = datetime.toUTC().toISO(); + utcDisplay.innerText = datetime.toUTC().toLocaleString(DateTime.DATETIME_MED); + durationDisplay.innerText = datetime.toRelative({ + rounding: 'expand' + }); + } + + const initValue = submission.value; + if (initValue) { + const initDate = DateTime.fromISO(initValue); + updateElements(initDate); + } + + picker.addEventListener('change', () => { + const newDate = DateTime.fromISO(picker.value); + updateElements(newDate); + }); +} + +function initSearchBar() { + const userSearchNode = document.getElementById('user-search'); + if (userSearchNode) { + userSearchNode.addEventListener('keyup', ({ key }) => { + if (key === 'Enter') { + const search = userSearchNode.value; + const params = new URLSearchParams({ search }); + window.location.href = `/admin/accounts?${params}`; + } + }); + } + + const communitySearchNode = document.getElementById('community-search'); + if (communitySearchNode) { + communitySearchNode.addEventListener('keyup', ({ key }) => { + if (key === 'Enter') { + const search = communitySearchNode.value; + const params = new URLSearchParams({ search }); + window.location.href = `/admin/communities?${params}`; + } + }); + } +} + +function removeReport(element) { + const id = element.getAttribute('data-id'); + const reason = prompt('Provide explanation for removing post:'); + if (!id || !reason) { + return; + } + + const params = new URLSearchParams({ reason }); + fetch(`/admin/${id}?${params}`, { + method: 'DELETE' + }) + .then(res => res.text()) + .then(_res => location.reload()); +} +window.removeReport = removeReport; + +function ignoreReport(element) { + const id = element.getAttribute('data-id'); + const reason = prompt('Provide explanation for ignoring this report:'); + if (!id || !reason) { + return; + } + + const params = new URLSearchParams({ reason }); + fetch(`/admin/${id}?${params}`, { + method: 'PUT' + }) + .then(res => res.text()) + .then(_res => location.reload()); +} +window.ignoreReport = ignoreReport; + +function initUploadPreview() { + const els = document.querySelectorAll('*[data-image-preview]'); + els.forEach((el) => { + el.addEventListener('change', () => { + const [file] = el.files; + const id = el.id; + const previews = document.querySelectorAll(`*[data-image-preview-for="${id}"]`); + previews.forEach((previewEl) => { + if (file) { + previewEl.src = URL.createObjectURL(file); + } + }); + }); + }); +} + +function savePNID(pid) { + const account_status = document.getElementById('account_status'); + const ban_lift_date = document.getElementById('ban_lift_date'); + const ban_reason = document.getElementById('ban_reason'); + fetch(`/admin/accounts/${pid}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + account_status: Number(account_status.value), + ban_lift_date: ban_lift_date.value, + ban_reason: ban_reason.value + }) + }) + .then(response => response.json()) + .then(({ error }) => { + if (!error) { + alert('Juxt user data saved'); + } + }) + .catch(console.log); +} +window.savePNID = savePNID; + +document.addEventListener('DOMContentLoaded', function () { + initSearchBar(); + initBanLiftDate(); + initUploadPreview(); +}); diff --git a/apps/juxtaposition-ui/webfiles/web/js/api.ts b/apps/juxtaposition-ui/webfiles/web/js/api.ts new file mode 100644 index 00000000..e7cfd1a1 --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/js/api.ts @@ -0,0 +1,54 @@ +import { POST, DELETE } from './xhr'; + +export type EmpathyPostResponse = { + status: number; + id: string; + count: number; +}; + +export function empathyPostById(id: string, cb: (post: EmpathyPostResponse) => void): void { + const params = 'postID=' + id; + + POST('/posts/empathy', params, (xhr) => { + if (xhr.status !== 200) { + return cb({ status: xhr.status, id, count: 0 }); + } + let post: EmpathyPostResponse; + + try { + post = JSON.parse(xhr.responseText); + } catch (e) { + console.error(e); + return cb({ status: 400, id, count: 0 }); + } + + cb(post); + }); +} + +export type DeletePostResponse = { + status: number; + nextUrl: string; +}; + +export function deletePostById(id: string, reason: string | null, cb: (result: DeletePostResponse) => void): void { + const postUrl = `/posts/${id}`; + const query = reason !== null ? `?reason=${encodeURIComponent(reason)}` : ''; + + DELETE(postUrl + query, function (xhr) { + if (xhr.status !== 200) { + return cb({ status: xhr.status, nextUrl: postUrl }); + } + + // HACK: we haven't actually formalised this API serverside yet + // for now, synthesise the response object ourselves + // make sure it looks like a URL... + const nextUrl = xhr.responseText; + if (!/^\/[\w/]+$/.test(nextUrl)) { + return cb({ status: 400, nextUrl: postUrl }); + } + + // make a response object + return cb({ status: 200, nextUrl }); + }); +} diff --git a/apps/juxtaposition-ui/src/webfiles/web/js/login.js b/apps/juxtaposition-ui/webfiles/web/js/login.js similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/js/login.js rename to apps/juxtaposition-ui/webfiles/web/js/login.js diff --git a/apps/juxtaposition-ui/src/webfiles/web/js/menus.js b/apps/juxtaposition-ui/webfiles/web/js/menus.js similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/js/menus.js rename to apps/juxtaposition-ui/webfiles/web/js/menus.js diff --git a/apps/juxtaposition-ui/src/webfiles/web/js/painting.js b/apps/juxtaposition-ui/webfiles/web/js/painting.js similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/js/painting.js rename to apps/juxtaposition-ui/webfiles/web/js/painting.js diff --git a/apps/juxtaposition-ui/src/webfiles/web/js/reports.js b/apps/juxtaposition-ui/webfiles/web/js/reports.js similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/js/reports.js rename to apps/juxtaposition-ui/webfiles/web/js/reports.js diff --git a/apps/juxtaposition-ui/src/webfiles/web/js/web.js b/apps/juxtaposition-ui/webfiles/web/js/web.js similarity index 92% rename from apps/juxtaposition-ui/src/webfiles/web/js/web.js rename to apps/juxtaposition-ui/webfiles/web/js/web.js index 1d774c14..d84d336b 100644 --- a/apps/juxtaposition-ui/src/webfiles/web/js/web.js +++ b/apps/juxtaposition-ui/webfiles/web/js/web.js @@ -1,7 +1,8 @@ import Pjax from 'pjax'; import { popupItemCb, setupPopup } from './menus'; import { initReportForm, reportPost } from './reports'; -import { deletePost } from './post'; +import { POST, GET } from './xhr'; +import { deletePostById, empathyPostById } from './api'; let pjax; setInterval(checkForUpdates, 30000); @@ -38,7 +39,6 @@ function initYeah() { const parent = document.getElementById(id); const count = document.getElementById('count-' + id); el.disabled = true; - const params = 'postID=' + id; if (el.classList.contains('selected')) { el.classList.remove('selected'); parent.classList.remove('yeah'); @@ -49,12 +49,9 @@ function initYeah() { count.innerText = ++count.innerText; } - POST('/posts/empathy', params, function a(data) { - const post = JSON.parse(data.response); - if (!post || post.status !== 200) { - // Apparently there was an actual error code for not being able to yeah a post, who knew! - // TODO: Find more of these - Toast(1155927); + empathyPostById(id, function (post) { + if (post.status !== 200) { + return Toast('You cannot give this post a Yeah!'); } el.disabled = false; count.innerText = post.count; @@ -104,17 +101,31 @@ function initPopupMenus() { popupItemCb(menu.querySelector('[data-action="report"]'), (_item, _ev) => { reportPost(post); }); - popupItemCb(menu.querySelector('[data-action="delete"]'), (item, _ev) => { + popupItemCb(menu.querySelector('[data-action="delete"]'), async (item, _ev) => { const moderator = item.getAttribute('data-moderator'); let reason = ''; if (moderator === 'true') { reason = prompt('Provide explanation for removing post:'); + } else { + reason = + confirm('Are you sure you want to delete your post? This cannot be undone.') + ? '' + : null; } if (reason == null) { return; // User canceled } - deletePost(post, reason); + deletePostById(post, reason, (result) => { + if (result.status !== 200) { + return alert(`Post was not able to be deleted. Please try again later. (${result.status})`); + } + + alert('Post has been deleted'); + if (moderator === true) { + window.location.href = result.nextUrl; + } + }); }); popupItemCb(menu.querySelector('[data-action="copy"]'), (_item, _ev) => { copyToClipboard(`${window.location.origin}/posts/${post}`); @@ -310,27 +321,6 @@ function checkForUpdates() { xhttp.open('GET', '/users/notifications.json', true); xhttp.send(); } -function POST(url, data, callback) { - const xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function () { - if (this.readyState === 4) { - return callback(this); - } - }; - xhttp.open('POST', url, true); - xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - xhttp.send(data); -} -function GET(url, callback) { - const xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function () { - if (this.readyState === 4) { - return callback(this); - } - }; - xhttp.open('GET', url, true); - xhttp.send(); -} window.onscroll = function (_ev) { if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight && document.getElementById('load-more')) { diff --git a/apps/juxtaposition-ui/webfiles/web/js/xhr.ts b/apps/juxtaposition-ui/webfiles/web/js/xhr.ts new file mode 100644 index 00000000..f89b98a8 --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/js/xhr.ts @@ -0,0 +1,29 @@ +export type XHRCallback = (request: XMLHttpRequest) => void; + +export function POST(url: string, data: string, callback: XHRCallback): void { + const xhttp = new XMLHttpRequest(); + xhttp.onload = function (): void { + callback(this); + }; + xhttp.open('POST', url, true); + xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + xhttp.send(data); +} + +export function GET(url: string, callback: XHRCallback): void { + const xhttp = new XMLHttpRequest(); + xhttp.onload = function (): void { + callback(this); + }; + xhttp.open('GET', url, true); + xhttp.send(); +} + +export function DELETE(url: string, callback: XHRCallback): void { + const xhttp = new XMLHttpRequest(); + xhttp.onload = function (): void { + callback(this); + }; + xhttp.open('DELETE', url, true); + xhttp.send(); +} diff --git a/apps/juxtaposition-ui/src/webfiles/web/manifest.json b/apps/juxtaposition-ui/webfiles/web/manifest.json similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/manifest.json rename to apps/juxtaposition-ui/webfiles/web/manifest.json diff --git a/apps/juxtaposition-ui/src/webfiles/web/me_page.ejs b/apps/juxtaposition-ui/webfiles/web/me_page.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/me_page.ejs rename to apps/juxtaposition-ui/webfiles/web/me_page.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/web/notifications.ejs b/apps/juxtaposition-ui/webfiles/web/notifications.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/notifications.ejs rename to apps/juxtaposition-ui/webfiles/web/notifications.ejs diff --git a/apps/juxtaposition-ui/webfiles/web/partials/assets/bin_icon.svg b/apps/juxtaposition-ui/webfiles/web/partials/assets/bin_icon.svg new file mode 100644 index 00000000..33a5be4c --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/partials/assets/bin_icon.svg @@ -0,0 +1 @@ + diff --git a/apps/juxtaposition-ui/webfiles/web/partials/assets/flag_icon.svg b/apps/juxtaposition-ui/webfiles/web/partials/assets/flag_icon.svg new file mode 100644 index 00000000..dd184d02 --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/partials/assets/flag_icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/juxtaposition-ui/webfiles/web/partials/assets/heart_icon.svg b/apps/juxtaposition-ui/webfiles/web/partials/assets/heart_icon.svg new file mode 100644 index 00000000..5f0d54cc --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/partials/assets/heart_icon.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/apps/juxtaposition-ui/webfiles/web/partials/assets/menu_icon.svg b/apps/juxtaposition-ui/webfiles/web/partials/assets/menu_icon.svg new file mode 100644 index 00000000..eed5ce0b --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/partials/assets/menu_icon.svg @@ -0,0 +1 @@ + diff --git a/apps/juxtaposition-ui/webfiles/web/partials/assets/reply_icon.svg b/apps/juxtaposition-ui/webfiles/web/partials/assets/reply_icon.svg new file mode 100644 index 00000000..b633a269 --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/partials/assets/reply_icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/juxtaposition-ui/webfiles/web/partials/assets/share_icon.svg b/apps/juxtaposition-ui/webfiles/web/partials/assets/share_icon.svg new file mode 100644 index 00000000..8e5338a6 --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/partials/assets/share_icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/juxtaposition-ui/src/webfiles/web/partials/following_list.ejs b/apps/juxtaposition-ui/webfiles/web/partials/following_list.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/partials/following_list.ejs rename to apps/juxtaposition-ui/webfiles/web/partials/following_list.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/web/partials/head.ejs b/apps/juxtaposition-ui/webfiles/web/partials/head.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/partials/head.ejs rename to apps/juxtaposition-ui/webfiles/web/partials/head.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/web/partials/more_posts.ejs b/apps/juxtaposition-ui/webfiles/web/partials/more_posts.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/partials/more_posts.ejs rename to apps/juxtaposition-ui/webfiles/web/partials/more_posts.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/web/partials/nav_bar.ejs b/apps/juxtaposition-ui/webfiles/web/partials/nav_bar.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/partials/nav_bar.ejs rename to apps/juxtaposition-ui/webfiles/web/partials/nav_bar.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/web/partials/notifications.ejs b/apps/juxtaposition-ui/webfiles/web/partials/notifications.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/partials/notifications.ejs rename to apps/juxtaposition-ui/webfiles/web/partials/notifications.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/web/partials/post_template.ejs b/apps/juxtaposition-ui/webfiles/web/partials/post_template.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/partials/post_template.ejs rename to apps/juxtaposition-ui/webfiles/web/partials/post_template.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/web/partials/posts_list.ejs b/apps/juxtaposition-ui/webfiles/web/partials/posts_list.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/partials/posts_list.ejs rename to apps/juxtaposition-ui/webfiles/web/partials/posts_list.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/web/partials/report_modal.ejs b/apps/juxtaposition-ui/webfiles/web/partials/report_modal.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/partials/report_modal.ejs rename to apps/juxtaposition-ui/webfiles/web/partials/report_modal.ejs diff --git a/apps/juxtaposition-ui/src/webfiles/web/robots.txt b/apps/juxtaposition-ui/webfiles/web/robots.txt similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/robots.txt rename to apps/juxtaposition-ui/webfiles/web/robots.txt diff --git a/apps/juxtaposition-ui/webfiles/web/tsconfig.json b/apps/juxtaposition-ui/webfiles/web/tsconfig.json new file mode 100644 index 00000000..f4049aa7 --- /dev/null +++ b/apps/juxtaposition-ui/webfiles/web/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "strict": true, + "sourceMap": true, + "resolveJsonModule": true, + "module": "ES2022", + "esModuleInterop": true, + "moduleResolution": "bundler", + "skipLibCheck": true, + "baseUrl": ".", + "outDir": "dist", + "allowJs": true, + "target": "es2022", + "noEmitOnError": true, + "noImplicitAny": true, + "strictPropertyInitialization": true, + "verbatimModuleSyntax": true, + "paths": { + "@/*": [ + "./*" + ] + } + }, + "include": [ + "js" + ] +} \ No newline at end of file diff --git a/apps/juxtaposition-ui/src/webfiles/web/user_page.ejs b/apps/juxtaposition-ui/webfiles/web/user_page.ejs similarity index 100% rename from apps/juxtaposition-ui/src/webfiles/web/user_page.ejs rename to apps/juxtaposition-ui/webfiles/web/user_page.ejs diff --git a/apps/miiverse-api/package.json b/apps/miiverse-api/package.json index dd9463dc..7cdac374 100644 --- a/apps/miiverse-api/package.json +++ b/apps/miiverse-api/package.json @@ -14,31 +14,31 @@ "test": "ts-node test/test.ts" }, "dependencies": { - "@aws-sdk/client-s3": "^3.958.0", - "@imagemagick/magick-wasm": "^0.0.37", + "@aws-sdk/client-s3": "^3.980.0", + "@imagemagick/magick-wasm": "^0.0.38", "@neato/config": "^4.1.0", - "@pretendonetwork/grpc": "^2.3.5", + "@pretendonetwork/grpc": "^2.4.1", "@repo/grpc-client": "^0.0.0", "colors": "^1.4.0", "crc": "^4.3.2", - "express": "^4.17.1", + "express": "^4.22.1", "express-async-errors": "^3.1.1", "express-prom-bundle": "^7.0.2", - "express-rate-limit": "^8.1.0", - "express-subdomain": "^1.0.5", + "express-rate-limit": "^8.2.1", + "express-subdomain": "^1.0.6", "moment": "^2.24.0", - "mongoose": "^8.19.2", + "mongoose": "^8.22.0", "multer": "^2.0.2", "nice-grpc": "^2.1.14", "node-snowflake": "0.0.1", "pako": "^2.1.0", "pino": "^10.1.0", "pino-http": "^11.0.0", - "pino-pretty": "^13.1.2", + "pino-pretty": "^13.1.3", "prom-client": "^15.1.3", - "supertest": "^7.1.4", + "supertest": "^7.2.2", "xmlbuilder": "^15.1.1", - "zod": "^4.1.13" + "zod": "^4.3.6" }, "devDependencies": { "@pretendonetwork/eslint-config": "^0.1.3", @@ -50,9 +50,9 @@ "@types/pako": "^2.0.4", "@types/supertest": "^6.0.3", "axios": "^1.13.2", - "eslint": "^9.39.1", - "newman": "^6.0.0", - "ora": "^9.0.0", + "eslint": "^9.39.2", + "newman": "^6.2.2", + "ora": "^9.1.0", "postman-collection": "^5.2.0", "table": "^6.8.1", "tsup": "^8.5.1", diff --git a/apps/miiverse-api/src/database.ts b/apps/miiverse-api/src/database.ts index e4a0e297..f3d1b4b2 100644 --- a/apps/miiverse-api/src/database.ts +++ b/apps/miiverse-api/src/database.ts @@ -7,10 +7,10 @@ import { Endpoint } from '@/models/endpoint'; import { Post } from '@/models/post'; import { Settings } from '@/models/settings'; import { config } from '@/config'; +import type { HydratedEndpointDocument } from '@/models/endpoint'; import type { HydratedConversationDocument } from '@/models/conversation'; import type { HydratedContentDocument } from '@/types/mongoose/content'; import type { HydratedSettingsDocument } from '@/types/mongoose/settings'; -import type { HydratedEndpointDocument } from '@/types/mongoose/endpoint'; import type { HydratedPostDocument, IPostInput } from '@/types/mongoose/post'; import type { HydratedCommunityDocument } from '@/types/mongoose/community'; diff --git a/apps/miiverse-api/src/models/endpoint.ts b/apps/miiverse-api/src/models/endpoint.ts index 19c3b2ab..13ebdf9d 100644 --- a/apps/miiverse-api/src/models/endpoint.ts +++ b/apps/miiverse-api/src/models/endpoint.ts @@ -1,15 +1,34 @@ import { Schema, model } from 'mongoose'; -import type { IEndpoint, EndpointModel } from '@/types/mongoose/endpoint'; +import type { HydratedDocument } from 'mongoose'; -const endpointSchema = new Schema({ +export type Endpoint = { + status: number; + server_access_level: string; + topics: boolean; + guest_access: boolean; + new_users: boolean; + host: string; + api_host: string; + portal_host: string; + n3ds_host: string; +} & Document; + +export type HydratedEndpointDocument = HydratedDocument; + +export const endpointSchema = new Schema({ status: Number, server_access_level: String, topics: Boolean, guest_access: Boolean, + new_users: { + type: Boolean, + default: true + }, host: String, api_host: String, portal_host: String, n3ds_host: String }); -export const Endpoint = model('Endpoint', endpointSchema); +export const Endpoint = model('Endpoint', endpointSchema); +export const ENDPOINT = Endpoint; diff --git a/apps/miiverse-api/src/models/report.ts b/apps/miiverse-api/src/models/report.ts index 436b645f..e6a15767 100644 --- a/apps/miiverse-api/src/models/report.ts +++ b/apps/miiverse-api/src/models/report.ts @@ -1,14 +1,39 @@ import { Schema, model } from 'mongoose'; import type { IReport, ReportModel } from '@/types/mongoose/report'; -const ReportSchema = new Schema({ - pid: String, - post_id: String, - reason: Number, +export const ReportSchema = new Schema({ + pid: { + type: Number, + required: true + }, + reported_by: { + type: Number, + required: true + }, + post_id: { + type: String, + required: true + }, + reason: { + type: Number, + required: true + }, + message: { + type: String, + required: true + }, created_at: { type: Date, + required: true, default: new Date() - } + }, + resolved: { + type: Boolean, + default: false + }, + note: String, + resolved_by: Number, + resolved_at: Date }); export const Report = model('Report', ReportSchema); diff --git a/apps/miiverse-api/src/services/discovery/routes/discovery.ts b/apps/miiverse-api/src/services/discovery/routes/discovery.ts index 231c1f55..71a6c51d 100644 --- a/apps/miiverse-api/src/services/discovery/routes/discovery.ts +++ b/apps/miiverse-api/src/services/discovery/routes/discovery.ts @@ -2,7 +2,7 @@ import express from 'express'; import xmlbuilder from 'xmlbuilder'; import { getEndpoint } from '@/database'; import { ApiErrorCode, badRequest, serverError } from '@/errors'; -import type { HydratedEndpointDocument } from '@/types/mongoose/endpoint'; +import type { HydratedEndpointDocument } from '@/models/endpoint'; const router = express.Router(); diff --git a/apps/miiverse-api/src/services/internal/routes/posts.ts b/apps/miiverse-api/src/services/internal/routes/posts.ts index 7490f3cb..d7eb60af 100644 --- a/apps/miiverse-api/src/services/internal/routes/posts.ts +++ b/apps/miiverse-api/src/services/internal/routes/posts.ts @@ -23,7 +23,8 @@ postsRouter.get('/posts', guards.guest, handle(async ({ req, res }) => { parent_id: postIdSchema.optional(), include_replies: z.stringbool().default(false), sort: z.enum(['newest', 'oldest']).default('newest') - }).and(pageSchema()).parse(req.query); + // Increased page limit for replies + }).and(pageSchema(500)).parse(req.query); if (query.parent_id && !query.include_replies) { throw new errors.badRequest('Please set include_replies=true to get replies to a parent'); diff --git a/apps/miiverse-api/src/types/mongoose/endpoint.ts b/apps/miiverse-api/src/types/mongoose/endpoint.ts deleted file mode 100644 index 9738a136..00000000 --- a/apps/miiverse-api/src/types/mongoose/endpoint.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Model, HydratedDocument } from 'mongoose'; - -export interface IEndpoint { - status: number; - server_access_level: string; - topics: boolean; - guest_access: boolean; - host: string; - api_host: string; - portal_host: string; - n3ds_host: string; -} - -export type EndpointModel = Model; - -export type HydratedEndpointDocument = HydratedDocument; diff --git a/apps/miiverse-api/src/types/mongoose/report.ts b/apps/miiverse-api/src/types/mongoose/report.ts index 413633a3..b6bd6178 100644 --- a/apps/miiverse-api/src/types/mongoose/report.ts +++ b/apps/miiverse-api/src/types/mongoose/report.ts @@ -1,10 +1,16 @@ import type { Model, HydratedDocument } from 'mongoose'; export interface IReport { - pid: string; + pid: number; + reported_by: number; post_id: string; reason: number; + message: string; created_at: Date; + resolved?: boolean | null; + note?: string | null; + resolved_by?: number | null; + resolved_at?: Date | null; } export type ReportModel = Model; diff --git a/package-lock.json b/package-lock.json index 288a2dbb..2ff1bf95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,28 +16,27 @@ "version": "3.0.0", "license": "AGPL-3.0-only", "dependencies": { - "@aws-sdk/client-s3": "^3.958.0", - "@imagemagick/magick-wasm": "^0.0.37", + "@aws-sdk/client-s3": "^3.980.0", + "@imagemagick/magick-wasm": "^0.0.38", "@neato/config": "^4.1.0", - "@pretendonetwork/grpc": "^2.3.5", + "@pretendonetwork/grpc": "^2.4.1", "@repo/grpc-client": "^0.0.0", "classnames": "^2.5.1", - "colors": "^1.4.0", "connect-redis": "^9.0.0", "cookie-parser": "^1.4.7", "crc": "^4.3.2", - "ejs": "^3.1.10", - "esbuild-plugin-copy": "^2.1.1", - "express": "^4.21.2", + "ejs": "^4.0.1", + "express": "^4.22.1", "express-async-errors": "^3.1.1", "express-prom-bundle": "^7.0.2", - "express-rate-limit": "^8.1.0", - "express-session": "^1.18.2", + "express-rate-limit": "^8.2.1", + "express-session": "^1.19.0", "express-subdomain": "^1.0.6", "hashmap": "^2.4.0", "luxon": "^3.7.2", + "method-override": "^3.0.0", "moment": "^2.30.1", - "mongoose": "^8.19.2", + "mongoose": "^8.22.0", "mongoose-fuzzy-search-next": "^1.0.13", "multer": "^2.0.2", "nice-grpc": "^2.1.14", @@ -45,20 +44,28 @@ "pako": "^2.1.0", "pino": "^10.1.0", "pino-http": "^11.0.0", - "pino-pretty": "^13.1.2", + "pino-pretty": "^13.1.3", "pjax": "^0.2.8", "prom-client": "^15.1.3", "react": "^19.1.1", - "react-dom": "^19.2.0", + "react-dom": "^19.2.4", "redis": "^5.10.0", - "tsx": "^4.20.6", - "zod": "^4.1.13" + "tsx": "^4.21.0", + "zod": "^4.3.6" }, "devDependencies": { + "@pretendonetwork/cave-types": "^1.0.2", "@pretendonetwork/eslint-config": "^0.1.3", + "@pretendonetwork/wiiu-browser-types": "^1.0.0", + "@pretendonetwork/wiiu-dialog-types": "^1.0.0", + "@pretendonetwork/wiiu-error-viewer-types": "^1.0.0", + "@repo/esbuild-plugin-oxipng": "^0.0.0", + "@repo/esbuild-plugin-spritesmith": "^0.0.0", + "@types/cookie-parser": "^1.4.10", "@types/express-session": "^1.18.2", "@types/hashmap": "^2.3.4", "@types/luxon": "^3.7.1", + "@types/method-override": "^3.0.0", "@types/node": "^22.19.1", "@types/react": "^19.1.13", "@types/react-dom": "^19.2.3", @@ -66,8 +73,9 @@ "browserslist-to-esbuild": "^2.1.1", "ejs-lint": "^2.0.1", "esbuild-fix-imports-plugin": "^1.0.23", + "esbuild-plugin-copy": "^2.1.1", "esbuild-raw-plugin": "^0.3.1", - "eslint": "^9.39.1", + "eslint": "^9.39.2", "globals": "^16.5.0", "nodemon": "^3.1.11", "npm-run-all": "^4.1.5", @@ -79,31 +87,31 @@ "version": "3.0.0", "license": "AGPL-3.0-only", "dependencies": { - "@aws-sdk/client-s3": "^3.958.0", - "@imagemagick/magick-wasm": "^0.0.37", + "@aws-sdk/client-s3": "^3.980.0", + "@imagemagick/magick-wasm": "^0.0.38", "@neato/config": "^4.1.0", - "@pretendonetwork/grpc": "^2.3.5", + "@pretendonetwork/grpc": "^2.4.1", "@repo/grpc-client": "^0.0.0", "colors": "^1.4.0", "crc": "^4.3.2", - "express": "^4.17.1", + "express": "^4.22.1", "express-async-errors": "^3.1.1", "express-prom-bundle": "^7.0.2", - "express-rate-limit": "^8.1.0", - "express-subdomain": "^1.0.5", + "express-rate-limit": "^8.2.1", + "express-subdomain": "^1.0.6", "moment": "^2.24.0", - "mongoose": "^8.19.2", + "mongoose": "^8.22.0", "multer": "^2.0.2", "nice-grpc": "^2.1.14", "node-snowflake": "0.0.1", "pako": "^2.1.0", "pino": "^10.1.0", "pino-http": "^11.0.0", - "pino-pretty": "^13.1.2", + "pino-pretty": "^13.1.3", "prom-client": "^15.1.3", - "supertest": "^7.1.4", + "supertest": "^7.2.2", "xmlbuilder": "^15.1.1", - "zod": "^4.1.13" + "zod": "^4.3.6" }, "devDependencies": { "@pretendonetwork/eslint-config": "^0.1.3", @@ -115,9 +123,9 @@ "@types/pako": "^2.0.4", "@types/supertest": "^6.0.3", "axios": "^1.13.2", - "eslint": "^9.39.1", - "newman": "^6.0.0", - "ora": "^9.0.0", + "eslint": "^9.39.2", + "newman": "^6.2.2", + "ora": "^9.1.0", "postman-collection": "^5.2.0", "table": "^6.8.1", "tsup": "^8.5.1", @@ -319,628 +327,628 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.958.0.tgz", - "integrity": "sha512-ol8Sw37AToBWb6PjRuT/Wu40SrrZSA0N4F7U3yTkjUNX0lirfO1VFLZ0hZtZplVJv8GNPITbiczxQ8VjxESXxg==", + "version": "3.980.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.980.0.tgz", + "integrity": "sha512-ch8QqKehyn1WOYbd8LyDbWjv84Z9OEj9qUxz8q3IOCU3ftAVkVR0wAuN96a1xCHnpOJcQZo3rOB08RlyKdkGxQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.957.0", - "@aws-sdk/credential-provider-node": "3.958.0", - "@aws-sdk/middleware-bucket-endpoint": "3.957.0", - "@aws-sdk/middleware-expect-continue": "3.957.0", - "@aws-sdk/middleware-flexible-checksums": "3.957.0", - "@aws-sdk/middleware-host-header": "3.957.0", - "@aws-sdk/middleware-location-constraint": "3.957.0", - "@aws-sdk/middleware-logger": "3.957.0", - "@aws-sdk/middleware-recursion-detection": "3.957.0", - "@aws-sdk/middleware-sdk-s3": "3.957.0", - "@aws-sdk/middleware-ssec": "3.957.0", - "@aws-sdk/middleware-user-agent": "3.957.0", - "@aws-sdk/region-config-resolver": "3.957.0", - "@aws-sdk/signature-v4-multi-region": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@aws-sdk/util-endpoints": "3.957.0", - "@aws-sdk/util-user-agent-browser": "3.957.0", - "@aws-sdk/util-user-agent-node": "3.957.0", - "@smithy/config-resolver": "^4.4.5", - "@smithy/core": "^3.20.0", - "@smithy/eventstream-serde-browser": "^4.2.7", - "@smithy/eventstream-serde-config-resolver": "^4.3.7", - "@smithy/eventstream-serde-node": "^4.2.7", - "@smithy/fetch-http-handler": "^5.3.8", - "@smithy/hash-blob-browser": "^4.2.8", - "@smithy/hash-node": "^4.2.7", - "@smithy/hash-stream-node": "^4.2.7", - "@smithy/invalid-dependency": "^4.2.7", - "@smithy/md5-js": "^4.2.7", - "@smithy/middleware-content-length": "^4.2.7", - "@smithy/middleware-endpoint": "^4.4.1", - "@smithy/middleware-retry": "^4.4.17", - "@smithy/middleware-serde": "^4.2.8", - "@smithy/middleware-stack": "^4.2.7", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/node-http-handler": "^4.4.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/credential-provider-node": "^3.972.4", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.3", + "@aws-sdk/middleware-expect-continue": "^3.972.3", + "@aws-sdk/middleware-flexible-checksums": "^3.972.3", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-location-constraint": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-sdk-s3": "^3.972.5", + "@aws-sdk/middleware-ssec": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.5", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/signature-v4-multi-region": "3.980.0", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.980.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.3", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/eventstream-serde-browser": "^4.2.8", + "@smithy/eventstream-serde-config-resolver": "^4.3.8", + "@smithy/eventstream-serde-node": "^4.2.8", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-blob-browser": "^4.2.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/hash-stream-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/md5-js": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.16", - "@smithy/util-defaults-mode-node": "^4.2.19", - "@smithy/util-endpoints": "^3.2.7", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-retry": "^4.2.7", - "@smithy/util-stream": "^4.5.8", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", - "@smithy/util-waiter": "^4.2.7", + "@smithy/util-waiter": "^4.2.8", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.958.0.tgz", - "integrity": "sha512-6qNCIeaMzKzfqasy2nNRuYnMuaMebCcCPP4J2CVGkA8QYMbIVKPlkn9bpB20Vxe6H/r3jtCCLQaOJjVTx/6dXg==", + "version": "3.980.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.980.0.tgz", + "integrity": "sha512-AhNXQaJ46C1I+lQ+6Kj+L24il5K9lqqIanJd8lMszPmP7bLnmX0wTKK0dxywcvrLdij3zhWttjAKEBNgLtS8/A==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.957.0", - "@aws-sdk/middleware-host-header": "3.957.0", - "@aws-sdk/middleware-logger": "3.957.0", - "@aws-sdk/middleware-recursion-detection": "3.957.0", - "@aws-sdk/middleware-user-agent": "3.957.0", - "@aws-sdk/region-config-resolver": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@aws-sdk/util-endpoints": "3.957.0", - "@aws-sdk/util-user-agent-browser": "3.957.0", - "@aws-sdk/util-user-agent-node": "3.957.0", - "@smithy/config-resolver": "^4.4.5", - "@smithy/core": "^3.20.0", - "@smithy/fetch-http-handler": "^5.3.8", - "@smithy/hash-node": "^4.2.7", - "@smithy/invalid-dependency": "^4.2.7", - "@smithy/middleware-content-length": "^4.2.7", - "@smithy/middleware-endpoint": "^4.4.1", - "@smithy/middleware-retry": "^4.4.17", - "@smithy/middleware-serde": "^4.2.8", - "@smithy/middleware-stack": "^4.2.7", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/node-http-handler": "^4.4.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.5", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.980.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.3", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.16", - "@smithy/util-defaults-mode-node": "^4.2.19", - "@smithy/util-endpoints": "^3.2.7", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-retry": "^4.2.7", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/core": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.957.0.tgz", - "integrity": "sha512-DrZgDnF1lQZv75a52nFWs6MExihJF2GZB6ETZRqr6jMwhrk2kbJPUtvgbifwcL7AYmVqHQDJBrR/MqkwwFCpiw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.957.0", - "@aws-sdk/xml-builder": "3.957.0", - "@smithy/core": "^3.20.0", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/signature-v4": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", + "version": "3.973.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.5.tgz", + "integrity": "sha512-IMM7xGfLGW6lMvubsA4j6BHU5FPgGAxoQ/NA63KqNLMwTS+PeMBcx8DPHL12Vg6yqOZnqok9Mu4H2BdQyq7gSA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/xml-builder": "^3.972.2", + "@smithy/core": "^3.22.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", - "@smithy/util-middleware": "^4.2.7", + "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/crc64-nvme": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.957.0.tgz", - "integrity": "sha512-qSwSfI+qBU9HDsd6/4fM9faCxYJx2yDuHtj+NVOQ6XYDWQzFab/hUdwuKZ77Pi6goLF1pBZhJ2azaC2w7LbnTA==", + "version": "3.972.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.0.tgz", + "integrity": "sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.957.0.tgz", - "integrity": "sha512-475mkhGaWCr+Z52fOOVb/q2VHuNvqEDixlYIkeaO6xJ6t9qR0wpLt4hOQaR6zR1wfZV0SlE7d8RErdYq/PByog==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.3.tgz", + "integrity": "sha512-OBYNY4xQPq7Rx+oOhtyuyO0AQvdJSpXRg7JuPNBJH4a1XXIzJQl4UHQTPKZKwfJXmYLpv4+OkcFen4LYmDPd3g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/types": "^4.11.0", + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.957.0.tgz", - "integrity": "sha512-8dS55QHRxXgJlHkEYaCGZIhieCs9NU1HU1BcqQ4RfUdSsfRdxxktqUKgCnBnOOn0oD3PPA8cQOCAVgIyRb3Rfw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@smithy/fetch-http-handler": "^5.3.8", - "@smithy/node-http-handler": "^4.4.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/util-stream": "^4.5.8", + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.5.tgz", + "integrity": "sha512-GpvBgEmSZPvlDekd26Zi+XsI27Qz7y0utUx0g2fSTSiDzhnd1FSa1owuodxR0BcUKNL7U2cOVhhDxgZ4iSoPVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/types": "^3.973.1", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.958.0.tgz", - "integrity": "sha512-u7twvZa1/6GWmPBZs6DbjlegCoNzNjBsMS/6fvh5quByYrcJr/uLd8YEr7S3UIq4kR/gSnHqcae7y2nL2bqZdg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/credential-provider-env": "3.957.0", - "@aws-sdk/credential-provider-http": "3.957.0", - "@aws-sdk/credential-provider-login": "3.958.0", - "@aws-sdk/credential-provider-process": "3.957.0", - "@aws-sdk/credential-provider-sso": "3.958.0", - "@aws-sdk/credential-provider-web-identity": "3.958.0", - "@aws-sdk/nested-clients": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/credential-provider-imds": "^4.2.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.3.tgz", + "integrity": "sha512-rMQAIxstP7cLgYfsRGrGOlpyMl0l8JL2mcke3dsIPLWke05zKOFyR7yoJzWCsI/QiIxjRbxpvPiAeKEA6CoYkg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/credential-provider-env": "^3.972.3", + "@aws-sdk/credential-provider-http": "^3.972.5", + "@aws-sdk/credential-provider-login": "^3.972.3", + "@aws-sdk/credential-provider-process": "^3.972.3", + "@aws-sdk/credential-provider-sso": "^3.972.3", + "@aws-sdk/credential-provider-web-identity": "^3.972.3", + "@aws-sdk/nested-clients": "3.980.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.958.0.tgz", - "integrity": "sha512-sDwtDnBSszUIbzbOORGh5gmXGl9aK25+BHb4gb1aVlqB+nNL2+IUEJA62+CE55lXSH8qXF90paivjK8tOHTwPA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/nested-clients": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.3.tgz", + "integrity": "sha512-Gc3O91iVvA47kp2CLIXOwuo5ffo1cIpmmyIewcYjAcvurdFHQ8YdcBe1KHidnbbBO4/ZtywGBACsAX5vr3UdoA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/nested-clients": "3.980.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.958.0.tgz", - "integrity": "sha512-vdoZbNG2dt66I7EpN3fKCzi6fp9xjIiwEA/vVVgqO4wXCGw8rKPIdDUus4e13VvTr330uQs2W0UNg/7AgtquEQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.957.0", - "@aws-sdk/credential-provider-http": "3.957.0", - "@aws-sdk/credential-provider-ini": "3.958.0", - "@aws-sdk/credential-provider-process": "3.957.0", - "@aws-sdk/credential-provider-sso": "3.958.0", - "@aws-sdk/credential-provider-web-identity": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/credential-provider-imds": "^4.2.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.4.tgz", + "integrity": "sha512-UwerdzosMSY7V5oIZm3NsMDZPv2aSVzSkZxYxIOWHBeKTZlUqW7XpHtJMZ4PZpJ+HMRhgP+MDGQx4THndgqJfQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.3", + "@aws-sdk/credential-provider-http": "^3.972.5", + "@aws-sdk/credential-provider-ini": "^3.972.3", + "@aws-sdk/credential-provider-process": "^3.972.3", + "@aws-sdk/credential-provider-sso": "^3.972.3", + "@aws-sdk/credential-provider-web-identity": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.957.0.tgz", - "integrity": "sha512-/KIz9kadwbeLy6SKvT79W81Y+hb/8LMDyeloA2zhouE28hmne+hLn0wNCQXAAupFFlYOAtZR2NTBs7HBAReJlg==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.3.tgz", + "integrity": "sha512-xkSY7zjRqeVc6TXK2xr3z1bTLm0wD8cj3lAkproRGaO4Ku7dPlKy843YKnHrUOUzOnMezdZ4xtmFc0eKIDTo2w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.958.0.tgz", - "integrity": "sha512-CBYHJ5ufp8HC4q+o7IJejCUctJXWaksgpmoFpXerbjAso7/Fg7LLUu9inXVOxlHKLlvYekDXjIUBXDJS2WYdgg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.958.0", - "@aws-sdk/core": "3.957.0", - "@aws-sdk/token-providers": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.3.tgz", + "integrity": "sha512-8Ww3F5Ngk8dZ6JPL/V5LhCU1BwMfQd3tLdoEuzaewX8FdnT633tPr+KTHySz9FK7fFPcz5qG3R5edVEhWQD4AA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.980.0", + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/token-providers": "3.980.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.958.0.tgz", - "integrity": "sha512-dgnvwjMq5Y66WozzUzxNkCFap+umHUtqMMKlr8z/vl9NYMLem/WUbWNpFFOVFWquXikc+ewtpBMR4KEDXfZ+KA==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.3.tgz", + "integrity": "sha512-62VufdcH5rRfiRKZRcf1wVbbt/1jAntMj1+J0qAd+r5pQRg2t0/P9/Rz16B1o5/0Se9lVL506LRjrhIJAhYBfA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/nested-clients": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/nested-clients": "3.980.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.957.0.tgz", - "integrity": "sha512-iczcn/QRIBSpvsdAS/rbzmoBpleX1JBjXvCynMbDceVLBIcVrwT1hXECrhtIC2cjh4HaLo9ClAbiOiWuqt+6MA==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.3.tgz", + "integrity": "sha512-fmbgWYirF67YF1GfD7cg5N6HHQ96EyRNx/rDIrTF277/zTWVuPI2qS/ZHgofwR1NZPe/NWvoppflQY01LrbVLg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.957.0", - "@aws-sdk/util-arn-parser": "3.957.0", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-arn-parser": "^3.972.2", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.957.0.tgz", - "integrity": "sha512-AlbK3OeVNwZZil0wlClgeI/ISlOt/SPUxBsIns876IFaVu/Pj3DgImnYhpcJuFRek4r4XM51xzIaGQXM6GDHGg==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.3.tgz", + "integrity": "sha512-4msC33RZsXQpUKR5QR4HnvBSNCPLGHmB55oDiROqqgyOc+TOfVu2xgi5goA7ms6MdZLeEh2905UfWMnMMF4mRg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.957.0.tgz", - "integrity": "sha512-iJpeVR5V8se1hl2pt+k8bF/e9JO4KWgPCMjg8BtRspNtKIUGy7j6msYvbDixaKZaF2Veg9+HoYcOhwnZumjXSA==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.972.3.tgz", + "integrity": "sha512-MkNGJ6qB9kpsLwL18kC/ZXppsJbftHVGCisqpEVbTQsum8CLYDX1Bmp/IvhRGNxsqCO2w9/4PwhDKBjG3Uvr4Q==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.957.0", - "@aws-sdk/crc64-nvme": "3.957.0", - "@aws-sdk/types": "3.957.0", + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/crc64-nvme": "3.972.0", + "@aws-sdk/types": "^3.973.1", "@smithy/is-array-buffer": "^4.2.0", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-stream": "^4.5.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.957.0.tgz", - "integrity": "sha512-BBgKawVyfQZglEkNTuBBdC3azlyqNXsvvN4jPkWAiNYcY0x1BasaJFl+7u/HisfULstryweJq/dAvIZIxzlZaA==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.3.tgz", + "integrity": "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.957.0.tgz", - "integrity": "sha512-y8/W7TOQpmDJg/fPYlqAhwA4+I15LrS7TwgUEoxogtkD8gfur9wFMRLT8LCyc9o4NMEcAnK50hSb4+wB0qv6tQ==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.3.tgz", + "integrity": "sha512-nIg64CVrsXp67vbK0U1/Is8rik3huS3QkRHn2DRDx4NldrEFMgdkZGI/+cZMKD9k4YOS110Dfu21KZLHrFA/1g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/types": "^4.11.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.957.0.tgz", - "integrity": "sha512-w1qfKrSKHf9b5a8O76yQ1t69u6NWuBjr5kBX+jRWFx/5mu6RLpqERXRpVJxfosbep7k3B+DSB5tZMZ82GKcJtQ==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.3.tgz", + "integrity": "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/types": "^4.11.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.957.0.tgz", - "integrity": "sha512-D2H/WoxhAZNYX+IjkKTdOhOkWQaK0jjJrDBj56hKjU5c9ltQiaX/1PqJ4dfjHntEshJfu0w+E6XJ+/6A6ILBBA==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.3.tgz", + "integrity": "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.957.0", + "@aws-sdk/types": "^3.973.1", "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.957.0.tgz", - "integrity": "sha512-5B2qY2nR2LYpxoQP0xUum5A1UNvH2JQpLHDH1nWFNF/XetV7ipFHksMxPNhtJJ6ARaWhQIDXfOUj0jcnkJxXUg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@aws-sdk/util-arn-parser": "3.957.0", - "@smithy/core": "^3.20.0", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/signature-v4": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.5.tgz", + "integrity": "sha512-3IgeIDiQ15tmMBFIdJ1cTy3A9rXHGo+b9p22V38vA3MozeMyVC8VmCYdDLA0iMWo4VHA9LDJTgCM0+xU3wjBOg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-arn-parser": "^3.972.2", + "@smithy/core": "^3.22.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-stream": "^4.5.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.957.0.tgz", - "integrity": "sha512-qwkmrK0lizdjNt5qxl4tHYfASh8DFpHXM1iDVo+qHe+zuslfMqQEGRkzxS8tJq/I+8F0c6v3IKOveKJAfIvfqQ==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.3.tgz", + "integrity": "sha512-dU6kDuULN3o3jEHcjm0c4zWJlY1zWVkjG9NPe9qxYLLpcbdj5kRYBS2DdWYD+1B9f910DezRuws7xDEqKkHQIg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/types": "^4.11.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.957.0.tgz", - "integrity": "sha512-50vcHu96XakQnIvlKJ1UoltrFODjsq2KvtTgHiPFteUS884lQnK5VC/8xd1Msz/1ONpLMzdCVproCQqhDTtMPQ==", + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.5.tgz", + "integrity": "sha512-TVZQ6PWPwQbahUI8V+Er+gS41ctIawcI/uMNmQtQ7RMcg3JYn6gyKAFKUb3HFYx2OjYlx1u11sETSwwEUxVHTg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@aws-sdk/util-endpoints": "3.957.0", - "@smithy/core": "^3.20.0", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.980.0", + "@smithy/core": "^3.22.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.958.0.tgz", - "integrity": "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw==", + "version": "3.980.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.980.0.tgz", + "integrity": "sha512-/dONY5xc5/CCKzOqHZCTidtAR4lJXWkGefXvTRKdSKMGaYbbKsxDckisd6GfnvPSLxWtvQzwgRGRutMRoYUApQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.957.0", - "@aws-sdk/middleware-host-header": "3.957.0", - "@aws-sdk/middleware-logger": "3.957.0", - "@aws-sdk/middleware-recursion-detection": "3.957.0", - "@aws-sdk/middleware-user-agent": "3.957.0", - "@aws-sdk/region-config-resolver": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@aws-sdk/util-endpoints": "3.957.0", - "@aws-sdk/util-user-agent-browser": "3.957.0", - "@aws-sdk/util-user-agent-node": "3.957.0", - "@smithy/config-resolver": "^4.4.5", - "@smithy/core": "^3.20.0", - "@smithy/fetch-http-handler": "^5.3.8", - "@smithy/hash-node": "^4.2.7", - "@smithy/invalid-dependency": "^4.2.7", - "@smithy/middleware-content-length": "^4.2.7", - "@smithy/middleware-endpoint": "^4.4.1", - "@smithy/middleware-retry": "^4.4.17", - "@smithy/middleware-serde": "^4.2.8", - "@smithy/middleware-stack": "^4.2.7", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/node-http-handler": "^4.4.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.5", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.980.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.3", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.16", - "@smithy/util-defaults-mode-node": "^4.2.19", - "@smithy/util-endpoints": "^3.2.7", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-retry": "^4.2.7", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.957.0.tgz", - "integrity": "sha512-V8iY3blh8l2iaOqXWW88HbkY5jDoWjH56jonprG/cpyqqCnprvpMUZWPWYJoI8rHRf2bqzZeql1slxG6EnKI7A==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.3.tgz", + "integrity": "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/config-resolver": "^4.4.5", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/types": "^4.11.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/config-resolver": "^4.4.6", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.957.0.tgz", - "integrity": "sha512-t6UfP1xMUigMMzHcb7vaZcjv7dA2DQkk9C/OAP1dKyrE0vb4lFGDaTApi17GN6Km9zFxJthEMUbBc7DL0hq1Bg==", + "version": "3.980.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.980.0.tgz", + "integrity": "sha512-tO2jBj+ZIVM0nEgi1SyxWtaYGpuAJdsrugmWcI3/U2MPWCYsrvKasUo0026NvJJao38wyUq9B8XTG8Xu53j/VA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@smithy/protocol-http": "^5.3.7", - "@smithy/signature-v4": "^5.3.7", - "@smithy/types": "^4.11.0", + "@aws-sdk/middleware-sdk-s3": "^3.972.5", + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.958.0.tgz", - "integrity": "sha512-UCj7lQXODduD1myNJQkV+LYcGYJ9iiMggR8ow8Hva1g3A/Na5imNXzz6O67k7DAee0TYpy+gkNw+SizC6min8Q==", + "version": "3.980.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.980.0.tgz", + "integrity": "sha512-1nFileg1wAgDmieRoj9dOawgr2hhlh7xdvcH57b1NnqfPaVlcqVJyPc6k3TLDUFPY69eEwNxdGue/0wIz58vjA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/nested-clients": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", + "@aws-sdk/core": "^3.973.5", + "@aws-sdk/nested-clients": "3.980.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/types": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.957.0.tgz", - "integrity": "sha512-wzWC2Nrt859ABk6UCAVY/WYEbAd7FjkdrQL6m24+tfmWYDNRByTJ9uOgU/kw9zqLCAwb//CPvrJdhqjTznWXAg==", + "version": "3.973.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.1.tgz", + "integrity": "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.957.0.tgz", - "integrity": "sha512-Aj6m+AyrhWyg8YQ4LDPg2/gIfGHCEcoQdBt5DeSFogN5k9mmJPOJ+IAmNSWmWRjpOxEy6eY813RNDI6qS97M0g==", + "version": "3.972.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.2.tgz", + "integrity": "sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.957.0.tgz", - "integrity": "sha512-xwF9K24mZSxcxKS3UKQFeX/dPYkEps9wF1b+MGON7EvnbcucrJGyQyK1v1xFPn1aqXkBTFi+SZaMRx5E5YCVFw==", + "version": "3.980.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.980.0.tgz", + "integrity": "sha512-AjKBNEc+rjOZQE1HwcD9aCELqg1GmUj1rtICKuY8cgwB73xJ4U/kNyqKKpN2k9emGqlfDY2D8itIp/vDc6OKpw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", - "@smithy/util-endpoints": "^3.2.7", + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-locate-window": { @@ -955,31 +963,31 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.957.0.tgz", - "integrity": "sha512-exueuwxef0lUJRnGaVkNSC674eAiWU07ORhxBnevFFZEKisln+09Qrtw823iyv5I1N8T+wKfh95xvtWQrNKNQw==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.3.tgz", + "integrity": "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/types": "^4.11.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.957.0.tgz", - "integrity": "sha512-ycbYCwqXk4gJGp0Oxkzf2KBeeGBdTxz559D41NJP8FlzSej1Gh7Rk40Zo6AyTfsNWkrl/kVi1t937OIzC5t+9Q==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.3.tgz", + "integrity": "sha512-gqG+02/lXQtO0j3US6EVnxtwwoXQC5l2qkhLCrqUrqdtcQxV7FDMbm9wLjKqoronSHyELGTjbFKK/xV5q1bZNA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/types": "^4.11.0", + "@aws-sdk/middleware-user-agent": "^3.972.5", + "@aws-sdk/types": "^3.973.1", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" }, "peerDependencies": { "aws-crt": ">=1.0.0" @@ -991,23 +999,23 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.957.0.tgz", - "integrity": "sha512-Ai5iiQqS8kJ5PjzMhWcLKN0G2yasAkvpnPlq2EnqlIMdB48HsizElt62qcktdxp4neRMyGkFq4NzgmDbXnhRiA==", + "version": "3.972.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.2.tgz", + "integrity": "sha512-jGOOV/bV1DhkkUhHiZ3/1GZ67cZyOXaDb7d1rYD6ZiXf5V9tBNOcgqXwRRPvrCbYaFRa1pPMFb3ZjqjWpR3YfA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws/lambda-invoke-store": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", - "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", + "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", "license": "Apache-2.0", "engines": { "node": ">=18.0.0" @@ -1158,9 +1166,10 @@ } }, "node_modules/@bufbuild/protobuf": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.4.0.tgz", - "integrity": "sha512-RN9M76x7N11QRihKovEglEjjVCQEA9PRBVnDgk9xw8JHLrcUrp4FpAVSPSH91cNbcTft3u2vpLN4GMbiKY9PJw==" + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz", + "integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==", + "license": "(Apache-2.0 AND BSD-3-Clause)" }, "node_modules/@colors/colors": { "version": "1.5.0", @@ -1207,12 +1216,13 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", - "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "aix" @@ -1222,12 +1232,13 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", - "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -1237,12 +1248,13 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", - "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -1252,12 +1264,13 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", - "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -1267,12 +1280,13 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", - "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1282,12 +1296,13 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", - "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1297,12 +1312,13 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", - "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1312,12 +1328,13 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", - "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1327,12 +1344,13 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", - "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1342,12 +1360,13 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", - "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1357,12 +1376,13 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", - "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1372,12 +1392,13 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", - "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "cpu": [ "loong64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1387,12 +1408,13 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", - "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "cpu": [ "mips64el" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1402,12 +1424,13 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", - "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1417,12 +1440,13 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", - "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "cpu": [ "riscv64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1432,12 +1456,13 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", - "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "cpu": [ "s390x" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1447,12 +1472,13 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", - "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1462,12 +1488,13 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", - "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -1477,12 +1504,13 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", - "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -1492,12 +1520,13 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", - "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -1507,12 +1536,13 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", - "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -1522,13 +1552,12 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", - "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1539,12 +1568,13 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", - "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "sunos" @@ -1554,12 +1584,13 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", - "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1569,12 +1600,13 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", - "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1584,12 +1616,13 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", - "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1746,10 +1779,11 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1780,9 +1814,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", - "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", "engines": { @@ -1929,78 +1963,11 @@ } }, "node_modules/@imagemagick/magick-wasm": { - "version": "0.0.37", - "resolved": "https://registry.npmjs.org/@imagemagick/magick-wasm/-/magick-wasm-0.0.37.tgz", - "integrity": "sha512-tVs9hcWu9u7I3Jz/XvUYVvCEniuxAR+JjZEzI+yKtQmYAtNsLF1WjoH1HZGCKPumaB9jAHZlcf2RGT9+1l3nxQ==", + "version": "0.0.38", + "resolved": "https://registry.npmjs.org/@imagemagick/magick-wasm/-/magick-wasm-0.0.38.tgz", + "integrity": "sha512-xg3q6ZMqUADyyy0h/1IndT9DUWUXY5lRhevF2WB+AYvphJ5yraH+R0IGO7H7DFnLSMrJ6zsxEbXfOnhNfark9w==", "license": "Apache-2.0" }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -2059,6 +2026,15 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/@jsquash/oxipng": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@jsquash/oxipng/-/oxipng-2.3.0.tgz", + "integrity": "sha512-aQ8wiEp6ztlTMXc+RMt/CG8crU3mEHDU+h+JYkIi6ctMhlh8+Ltj5XwQFfBuyzKYrp8NxaFW80Dp824bqjr+zA==", + "license": "Apache-2.0", + "dependencies": { + "wasm-feature-detect": "^1.2.11" + } + }, "node_modules/@mongodb-js/saslprep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz", @@ -2113,6 +2089,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -2125,6 +2102,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "engines": { "node": ">= 8" } @@ -2133,6 +2111,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -2202,9 +2181,9 @@ } }, "node_modules/@paralleldrive/cuid2": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", - "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", "license": "MIT", "dependencies": { "@noble/hashes": "^1.1.5" @@ -2216,16 +2195,6 @@ "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", "license": "MIT" }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@postman/form-data": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", @@ -2265,10 +2234,11 @@ } }, "node_modules/@postman/tunnel-agent": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.4.tgz", - "integrity": "sha512-CJJlq8V7rNKhAw4sBfjixKpJW00SHqebqNUQKxMoepgeWZIbdPcD+rguRcivGhS4N12PymDcKgUgSD4rVC+RjQ==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.8.tgz", + "integrity": "sha512-2U42SmZW5G+suEcS++zB94sBWNO4qD4bvETGFRFDTqSpYl5ksfjcPqzYpgQgXgUmb6dfz+fAGbkcRamounGm0w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" }, @@ -2276,6 +2246,12 @@ "node": "*" } }, + "node_modules/@pretendonetwork/cave-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pretendonetwork/cave-types/-/cave-types-1.0.2.tgz", + "integrity": "sha512-DFM/osKDWGS4EvVRVDlsss0u+uffN1QiNLlVqVJqhQGz/Fgh14j9Yx9n2rUJNSI/LdEtW7FreDyCGwvJXqbcCQ==", + "dev": true + }, "node_modules/@pretendonetwork/eslint-config": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@pretendonetwork/eslint-config/-/eslint-config-0.1.3.tgz", @@ -2296,9 +2272,9 @@ } }, "node_modules/@pretendonetwork/grpc": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.5.tgz", - "integrity": "sha512-FU0uvhZr8gFgiIi+gBtD6+5or34bHcd9X+ZVpRdc7IiupW5V+uxiXigJKX4Vd46QC412y+BEGofCjM1bBlTJhg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.4.1.tgz", + "integrity": "sha512-5odC6eT5czKrHJkjYPSs5CRiSvwdOaO0K8JvxsVeGD5A+LAQePeG9U8Lm3YHAg1o2tX9Vk2aCRh7KiHjs8qkrQ==", "license": "AGPL-3.0-only", "dependencies": { "@bufbuild/protobuf": "^2.2.2", @@ -2306,6 +2282,24 @@ "typescript": "^5.7.2" } }, + "node_modules/@pretendonetwork/wiiu-browser-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pretendonetwork/wiiu-browser-types/-/wiiu-browser-types-1.0.0.tgz", + "integrity": "sha512-P+F51y7rY4E3+Wb72w8sMESFowa1DcwJKxtt5NFlKUyQ4jFCf6sf3ZXMwtdUFzkLyRrjiH6soq5/aYyUMb1wDA==", + "dev": true + }, + "node_modules/@pretendonetwork/wiiu-dialog-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pretendonetwork/wiiu-dialog-types/-/wiiu-dialog-types-1.0.0.tgz", + "integrity": "sha512-2jPUnIVHPwfDkIL+fKwYL9vlpJfG+tbp8TjJLdNvl5LZ+akcm1zxEgkxVz9qzs9buWOJim7Qnn8ftw7ds7Ljqg==", + "dev": true + }, + "node_modules/@pretendonetwork/wiiu-error-viewer-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pretendonetwork/wiiu-error-viewer-types/-/wiiu-error-viewer-types-1.0.0.tgz", + "integrity": "sha512-tS6F/B5Og73/n1mqbe3SGuUcNIPpJR6rTrD3rIb4Hf9jROyC6BXteNjuKew8nLq6Xp14hA7QF/1el5n9S4h4fQ==", + "dev": true + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -2430,6 +2424,14 @@ "@redis/client": "^5.10.0" } }, + "node_modules/@repo/esbuild-plugin-oxipng": { + "resolved": "packages/esbuild-plugin-oxipng", + "link": true + }, + "node_modules/@repo/esbuild-plugin-spritesmith": { + "resolved": "packages/esbuild-plugin-spritesmith", + "link": true + }, "node_modules/@repo/grpc-client": { "resolved": "packages/grpc-client", "link": true @@ -2719,12 +2721,12 @@ "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, "node_modules/@smithy/abort-controller": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.7.tgz", - "integrity": "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", + "integrity": "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -2757,16 +2759,16 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.5.tgz", - "integrity": "sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", + "integrity": "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.7", - "@smithy/types": "^4.11.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-endpoints": "^3.2.7", - "@smithy/util-middleware": "^4.2.7", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" }, "engines": { @@ -2774,18 +2776,18 @@ } }, "node_modules/@smithy/core": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.20.0.tgz", - "integrity": "sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ==", + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.22.0.tgz", + "integrity": "sha512-6vjCHD6vaY8KubeNw2Fg3EK0KLGQYdldG4fYgQmA0xSW0dJ8G2xFhSOdrlUakWVoP5JuWHtFODg3PNd/DN3FDA==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.2.8", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-stream": "^4.5.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" @@ -2795,15 +2797,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.7.tgz", - "integrity": "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", + "integrity": "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", "tslib": "^2.6.2" }, "engines": { @@ -2811,13 +2813,13 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.7.tgz", - "integrity": "sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.8.tgz", + "integrity": "sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" }, @@ -2826,13 +2828,13 @@ } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.7.tgz", - "integrity": "sha512-ujzPk8seYoDBmABDE5YqlhQZAXLOrtxtJLrbhHMKjBoG5b4dK4i6/mEU+6/7yXIAkqOO8sJ6YxZl+h0QQ1IJ7g==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.8.tgz", + "integrity": "sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.2.7", - "@smithy/types": "^4.11.0", + "@smithy/eventstream-serde-universal": "^4.2.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -2840,12 +2842,12 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.7.tgz", - "integrity": "sha512-x7BtAiIPSaNaWuzm24Q/mtSkv+BrISO/fmheiJ39PKRNH3RmH2Hph/bUKSOBOBC9unqfIYDhKTHwpyZycLGPVQ==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.8.tgz", + "integrity": "sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -2853,13 +2855,13 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.7.tgz", - "integrity": "sha512-roySCtHC5+pQq5lK4be1fZ/WR6s/AxnPaLfCODIPArtN2du8s5Ot4mKVK3pPtijL/L654ws592JHJ1PbZFF6+A==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.8.tgz", + "integrity": "sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.2.7", - "@smithy/types": "^4.11.0", + "@smithy/eventstream-serde-universal": "^4.2.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -2867,13 +2869,13 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.7.tgz", - "integrity": "sha512-QVD+g3+icFkThoy4r8wVFZMsIP08taHVKjE6Jpmz8h5CgX/kk6pTODq5cht0OMtcapUx+xrPzUTQdA+TmO0m1g==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.8.tgz", + "integrity": "sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^4.2.7", - "@smithy/types": "^4.11.0", + "@smithy/eventstream-codec": "^4.2.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -2881,14 +2883,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.8.tgz", - "integrity": "sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", + "integrity": "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.7", - "@smithy/querystring-builder": "^4.2.7", - "@smithy/types": "^4.11.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, @@ -2897,14 +2899,14 @@ } }, "node_modules/@smithy/hash-blob-browser": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.8.tgz", - "integrity": "sha512-07InZontqsM1ggTCPSRgI7d8DirqRrnpL7nIACT4PW0AWrgDiHhjGZzbAE5UtRSiU0NISGUYe7/rri9ZeWyDpw==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.9.tgz", + "integrity": "sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==", "license": "Apache-2.0", "dependencies": { "@smithy/chunked-blob-reader": "^5.2.0", "@smithy/chunked-blob-reader-native": "^4.2.1", - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -2912,12 +2914,12 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.7.tgz", - "integrity": "sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz", + "integrity": "sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -2927,12 +2929,12 @@ } }, "node_modules/@smithy/hash-stream-node": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.7.tgz", - "integrity": "sha512-ZQVoAwNYnFMIbd4DUc517HuwNelJUY6YOzwqrbcAgCnVn+79/OK7UjwA93SPpdTOpKDVkLIzavWm/Ck7SmnDPQ==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.8.tgz", + "integrity": "sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, @@ -2941,12 +2943,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.7.tgz", - "integrity": "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz", + "integrity": "sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -2966,12 +2968,12 @@ } }, "node_modules/@smithy/md5-js": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.7.tgz", - "integrity": "sha512-Wv6JcUxtOLTnxvNjDnAiATUsk8gvA6EeS8zzHig07dotpByYsLot+m0AaQEniUBjx97AC41MQR4hW0baraD1Xw==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.8.tgz", + "integrity": "sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, @@ -2980,13 +2982,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.7.tgz", - "integrity": "sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz", + "integrity": "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -2994,18 +2996,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.1.tgz", - "integrity": "sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg==", + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.12.tgz", + "integrity": "sha512-9JMKHVJtW9RysTNjcBZQHDwB0p3iTP6B1IfQV4m+uCevkVd/VuLgwfqk5cnI4RHcp4cPwoIvxQqN4B1sxeHo8Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.20.0", - "@smithy/middleware-serde": "^4.2.8", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", - "@smithy/util-middleware": "^4.2.7", + "@smithy/core": "^3.22.0", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" }, "engines": { @@ -3013,18 +3015,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.17", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.17.tgz", - "integrity": "sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/service-error-classification": "^4.2.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-retry": "^4.2.7", + "version": "4.4.29", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.29.tgz", + "integrity": "sha512-bmTn75a4tmKRkC5w61yYQLb3DmxNzB8qSVu9SbTYqW6GAL0WXO2bDZuMAn/GJSbOdHEdjZvWxe+9Kk015bw6Cg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/service-error-classification": "^4.2.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, @@ -3033,13 +3035,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.8.tgz", - "integrity": "sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", + "integrity": "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3047,12 +3049,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.7.tgz", - "integrity": "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", + "integrity": "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3060,14 +3062,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.7.tgz", - "integrity": "sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", + "integrity": "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3075,15 +3077,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.4.7", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.7.tgz", - "integrity": "sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==", + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.8.tgz", + "integrity": "sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/querystring-builder": "^4.2.7", - "@smithy/types": "^4.11.0", + "@smithy/abort-controller": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3091,12 +3093,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.7.tgz", - "integrity": "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz", + "integrity": "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3104,12 +3106,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.7.tgz", - "integrity": "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==", + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", + "integrity": "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3117,12 +3119,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.7.tgz", - "integrity": "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", + "integrity": "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" }, @@ -3131,12 +3133,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.7.tgz", - "integrity": "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", + "integrity": "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3144,24 +3146,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.7.tgz", - "integrity": "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", + "integrity": "sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0" + "@smithy/types": "^4.12.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.2.tgz", - "integrity": "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz", + "integrity": "sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3169,16 +3171,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.7.tgz", - "integrity": "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg==", + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz", + "integrity": "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.7", + "@smithy/util-middleware": "^4.2.8", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -3188,17 +3190,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.2.tgz", - "integrity": "sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.11.1.tgz", + "integrity": "sha512-SERgNg5Z1U+jfR6/2xPYjSEHY1t3pyTHC/Ma3YQl6qWtmiL42bvNId3W/oMUWIwu7ekL2FMPdqAmwbQegM7HeQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.20.0", - "@smithy/middleware-endpoint": "^4.4.1", - "@smithy/middleware-stack": "^4.2.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "@smithy/util-stream": "^4.5.8", + "@smithy/core": "^3.22.0", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" }, "engines": { @@ -3206,9 +3208,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.11.0.tgz", - "integrity": "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz", + "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3218,13 +3220,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.7.tgz", - "integrity": "sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz", + "integrity": "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.2.7", - "@smithy/types": "^4.11.0", + "@smithy/querystring-parser": "^4.2.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3295,14 +3297,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.16", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.16.tgz", - "integrity": "sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ==", + "version": "4.3.28", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.28.tgz", + "integrity": "sha512-/9zcatsCao9h6g18p/9vH9NIi5PSqhCkxQ/tb7pMgRFnqYp9XUOyOlGPDMHzr8n5ih6yYgwJEY2MLEobUgi47w==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3310,17 +3312,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.19", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.19.tgz", - "integrity": "sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA==", + "version": "4.2.31", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.31.tgz", + "integrity": "sha512-JTvoApUXA5kbpceI2vuqQzRjeTbLpx1eoa5R/YEZbTgtxvIB7AQZxFJ0SEyfCpgPCyVV9IT7we+ytSeIB3CyWA==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.4.5", - "@smithy/credential-provider-imds": "^4.2.7", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", + "@smithy/config-resolver": "^4.4.6", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3328,13 +3330,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.7.tgz", - "integrity": "sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg==", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", + "integrity": "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.7", - "@smithy/types": "^4.11.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3354,12 +3356,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.7.tgz", - "integrity": "sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", + "integrity": "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.11.0", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3367,13 +3369,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.7.tgz", - "integrity": "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz", + "integrity": "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.7", - "@smithy/types": "^4.11.0", + "@smithy/service-error-classification": "^4.2.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3381,14 +3383,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.8", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.8.tgz", - "integrity": "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==", + "version": "4.5.10", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.10.tgz", + "integrity": "sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.8", - "@smithy/node-http-handler": "^4.4.7", - "@smithy/types": "^4.11.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", @@ -3425,13 +3427,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.7.tgz", - "integrity": "sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.8.tgz", + "integrity": "sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.7", - "@smithy/types": "^4.11.0", + "@smithy/abort-controller": "^4.2.8", + "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "engines": { @@ -3528,6 +3530,16 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.10.tgz", + "integrity": "sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, "node_modules/@types/cookiejar": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", @@ -3540,6 +3552,13 @@ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true }, + "node_modules/@types/expect": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", + "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -3622,6 +3641,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-7XFHR6j7JljprBpzzRZatakUXm1kEGAM3PL/GSsGRHtDvOAKYCdmnXX/5YSl1eQrpJymGs9tRekSWEGaG+Ntjw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -3727,6 +3756,16 @@ "@types/send": "*" } }, + "node_modules/@types/spritesmith": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@types/spritesmith/-/spritesmith-3.4.5.tgz", + "integrity": "sha512-LRyCIJ+7LW59eFnUVfF4Wwhs23MceEqFQQtV7l7hHYz/z0zeN24J5TJOE2qZjaUOsGcSZ0ISDTIhe2dvpWrnmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/vinyl": "*" + } + }, "node_modules/@types/superagent": { "version": "8.1.9", "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", @@ -3755,6 +3794,17 @@ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "dev": true }, + "node_modules/@types/vinyl": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", + "integrity": "sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/expect": "^1.20.4", + "@types/node": "*" + } + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -4345,6 +4395,16 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4393,6 +4453,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -4405,6 +4466,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -4471,6 +4533,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, "engines": { "node": ">=8" } @@ -4700,7 +4763,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -4735,10 +4798,17 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bin-pack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bin-pack/-/bin-pack-1.0.2.tgz", + "integrity": "sha512-aOk0SxEon5LF9cMxQFViSKb4qccG6rs7XKyMXIb1J8f8LA2acTIWnHdT0IOTe4gYBbqgjdbuTZ5f+UP+vlh4Mw==", + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "engines": { "node": ">=8" }, @@ -4758,22 +4828,23 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", @@ -4793,20 +4864,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/body-parser/node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/bowser": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", @@ -4814,9 +4871,10 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -4825,6 +4883,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -4903,58 +4962,12 @@ "node": ">=16.20.1" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "peer": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, - "node_modules/buffer/node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "peer": true - }, "node_modules/bundle-require": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", @@ -4985,6 +4998,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5125,6 +5139,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -5234,6 +5249,21 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==", + "license": "MIT" + }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -5369,6 +5399,36 @@ "node": ">= 0.6" } }, + "node_modules/contentstream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/contentstream/-/contentstream-1.0.0.tgz", + "integrity": "sha512-jqWbfFZFG9tZbdej7+TzXI4kanABh3BLtTWY6NxqTK5zo6iTIeo5aq4iRVfYsLQ0y8ccQqmJR/J4NeMmEdnR2w==", + "license": "MIT", + "dependencies": { + "readable-stream": "~1.0.33-1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/contentstream/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/contentstream/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", @@ -5400,6 +5460,12 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/crc": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/crc/-/crc-4.3.2.tgz", @@ -5443,6 +5509,15 @@ "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==", "dev": true }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "license": "MIT", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -5455,6 +5530,12 @@ "node": ">=0.10" } }, + "node_modules/data-uri-to-buffer": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz", + "integrity": "sha512-Cp+jOa8QJef5nXS5hU7M1DWzXPEIoVR3kbV0dQuVGwROZg8bGf1DcCnkmajBTnvghTtSNMUdRrPjgaT6ZQucbw==", + "license": "MIT" + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -5633,6 +5714,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, "dependencies": { "path-type": "^4.0.0" }, @@ -5686,12 +5768,6 @@ "node": ">= 0.4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -5714,17 +5790,18 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-4.0.1.tgz", + "integrity": "sha512-krvQtxc0btwSm/nvnt1UpnaFDFVJpJ0fdckmALpCgShsr/iGYHTnJiUliZTgmzq/UxTX33TtOQVKaNigMQp/6Q==", + "license": "Apache-2.0", "dependencies": { - "jake": "^10.8.5" + "jake": "^10.9.1" }, "bin": { "ejs": "bin/cli.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.12.18" } }, "node_modules/ejs-include-regex": { @@ -5767,6 +5844,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/ejs-lint/node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ejs-lint/node_modules/globby": { "version": "13.2.2", "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", @@ -6025,10 +6118,11 @@ } }, "node_modules/esbuild": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", - "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -6036,31 +6130,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/esbuild-fix-imports-plugin": { @@ -6074,6 +6169,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/esbuild-plugin-copy/-/esbuild-plugin-copy-2.1.1.tgz", "integrity": "sha512-Bk66jpevTcV8KMFzZI1P7MZKZ+uDcrZm2G2egZ2jNIvVnivDpodZI+/KnpL3Jnap0PBdIHU7HwFGB8r+vV5CVw==", + "dev": true, "dependencies": { "chalk": "^4.1.2", "chokidar": "^3.5.3", @@ -6088,6 +6184,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -6140,9 +6237,9 @@ } }, "node_modules/eslint": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", - "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "dependencies": { @@ -6152,7 +6249,7 @@ "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.1", + "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -6342,10 +6439,11 @@ } }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6492,10 +6590,11 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6595,38 +6694,39 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -6666,9 +6766,9 @@ } }, "node_modules/express-rate-limit": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.1.0.tgz", - "integrity": "sha512-4nLnATuKupnmwqiJc27b4dCFmB/T60ExgmtDD7waf4LdrbJ8CPZzZRHYErDYNhoz+ql8fUdYwM/opf90PoPAQA==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", + "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", "license": "MIT", "dependencies": { "ip-address": "10.0.1" @@ -6683,32 +6783,27 @@ "express": ">= 4.11" } }, - "node_modules/express-rate-limit/node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, "node_modules/express-session": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", - "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.19.0.tgz", + "integrity": "sha512-0csaMkGq+vaiZTmSMMGkfdCOabYv192VbytFypcvI0MANrp+4i/7yEkJ0sbAEhycQjntaKGzYfjfXQyVb7BHMA==", "license": "MIT", "dependencies": { - "cookie": "0.7.2", - "cookie-signature": "1.0.7", - "debug": "2.6.9", + "cookie": "~0.7.2", + "cookie-signature": "~1.0.7", + "debug": "~2.6.9", "depd": "~2.0.0", "on-headers": "~1.1.0", "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", + "safe-buffer": "~5.2.1", "uid-safe": "~2.1.5" }, "engines": { "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express-session/node_modules/cookie-signature": { @@ -6734,14 +6829,6 @@ "resolved": "https://registry.npmjs.org/express-subdomain/-/express-subdomain-1.0.6.tgz", "integrity": "sha512-A51MvQhk5L3aDV4s5XcKrn5I0bCFOk95xikJjlDWNUrtiAaCjy44RjvBz4uLRH0nCvt8Y3SKt1EvxUUmGJ5QRQ==" }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -6771,9 +6858,10 @@ ] }, "node_modules/fast-copy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", - "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.2.tgz", + "integrity": "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==", + "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -6785,6 +6873,7 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -6851,15 +6940,20 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -6913,6 +7007,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -6966,6 +7061,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha512-ArRi5axuv66gEsyl3UuK80CzW7t56hem73YGNYxNWTGNKFJUadSb9Gu9SHijYEUi8ulQMf1bJomYNwSCPHhtTQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fix-dts-default-cjs-exports": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", @@ -7032,22 +7136,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -7058,9 +7146,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -7200,6 +7288,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-pixels": { + "version": "3.3.3", + "resolved": "git+ssh://git@github.com/scijs/get-pixels.git#5283976c8dda7a2f9a85221aaf2f2a8b3b7a5b42", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "0.0.3", + "jpeg-js": "^0.4.1", + "mime-types": "^2.0.1", + "ndarray": "^1.0.13", + "ndarray-pack": "^1.1.1", + "node-bitmap": "0.0.1", + "omggif": "^1.0.5", + "parse-data-uri": "^0.2.0", + "pngjs": "^7.0.0", + "through": "^2.3.4" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -7250,30 +7355,40 @@ "assert-plus": "^1.0.0" } }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, + "node_modules/gif-encoder": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gif-encoder/-/gif-encoder-0.4.3.tgz", + "integrity": "sha512-HMfSa+EIng62NbDhM63QGYoc49/m8DcZ9hhBtw+CXX9mKboSpeFVxjZ2WEWaMFZ14MUjfACK7jsrxrJffIVrCg==", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "readable-stream": "~1.1.9" }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/gif-encoder/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, + "node_modules/gif-encoder/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -7314,6 +7429,7 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -7356,7 +7472,6 @@ "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", @@ -7500,16 +7615,30 @@ "license": "ISC" }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7572,6 +7701,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -7583,6 +7713,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, "engines": { "node": ">= 4" } @@ -7638,6 +7769,21 @@ "node": ">= 0.4" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "license": "MIT" + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -7701,6 +7847,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -7724,6 +7871,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-bun-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", @@ -7798,6 +7951,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -7847,6 +8001,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -7896,6 +8051,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { "node": ">=0.12.0" } @@ -8028,6 +8184,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "license": "MIT" + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -8071,6 +8233,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -8100,21 +8268,6 @@ "node": ">= 0.4" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jake": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", @@ -8133,9 +8286,10 @@ } }, "node_modules/jake/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8181,6 +8335,12 @@ "node": ">=10" } }, + "node_modules/jpeg-js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", + "license": "BSD-3-Clause" + }, "node_modules/js-md4": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", @@ -8265,6 +8425,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, "dependencies": { "universalify": "^2.0.0" }, @@ -8324,6 +8485,17 @@ "json-buffer": "3.0.1" } }, + "node_modules/layout": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/layout/-/layout-2.2.0.tgz", + "integrity": "sha512-+kdgg25XW11BA4cl9vF+SH01HaBipld2Nf/PlU2kSYncAbdUbDoahzrlh6yhR93N/wR2TGgcFoxebzR1LKmZUg==", + "dependencies": { + "bin-pack": "~1.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -8463,12 +8635,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, "node_modules/luxon": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", @@ -8544,22 +8710,54 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "engines": { "node": ">= 8" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "node_modules/method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "license": "MIT", + "dependencies": { + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" + }, "engines": { - "node": ">= 0.6" + "node": ">= 0.10" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "node_modules/method-override/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/method-override/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -8572,6 +8770,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -8664,15 +8863,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -8762,9 +8952,9 @@ } }, "node_modules/mongoose": { - "version": "8.19.2", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.19.2.tgz", - "integrity": "sha512-ww2T4dBV+suCbOfG5YPwj9pLCfUVyj8FEA1D3Ux1HHqutpLxGyOYEPU06iPRBW4cKr3PJfOSYsIpHWPTkz5zig==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.22.0.tgz", + "integrity": "sha512-LKTPPqD3CVcSZJRzPcwKiSVYTmAvBZeVT0V34vUiqPEo9sBmOEg1y4TpDbUb90Zf2lO4N05ailQnKxiapCN08g==", "license": "MIT", "dependencies": { "bson": "^6.10.4", @@ -8864,6 +9054,35 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "license": "MIT", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pack": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz", + "integrity": "sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==", + "license": "MIT", + "dependencies": { + "cwise-compiler": "^1.1.2", + "ndarray": "^1.0.13" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -8875,14 +9094,14 @@ "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/newman": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/newman/-/newman-6.2.1.tgz", - "integrity": "sha512-Zq8Sr5GFF+OXs5yIbyglLMKMh1WNMjYVV0yZaSBZ+DIgQOIWcxT8QTfbrl/YUGrLyT4rjpu+yZ/Z+kozw79GEA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/newman/-/newman-6.2.2.tgz", + "integrity": "sha512-BmGzMz6f2FLtw/hHAbhEAVqXS+3APJGAWzlxVijSElFaxC37wpHEqsOB09d/2uHMvTyMXGArtbFa+z5m/a68Uw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@postman/tough-cookie": "4.1.3-postman.1", "async": "3.2.5", @@ -8898,7 +9117,7 @@ "mkdirp": "3.0.1", "postman-collection": "4.4.0", "postman-collection-transformer": "4.1.8", - "postman-request": "2.88.1-postman.34", + "postman-request": "2.88.1-postman.48", "postman-runtime": "7.39.1", "pretty-ms": "7.0.1", "semver": "7.6.3", @@ -8928,6 +9147,21 @@ "node": ">=0.10.0" } }, + "node_modules/newman/node_modules/http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/newman/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -9004,6 +9238,38 @@ "node": ">=10" } }, + "node_modules/newman/node_modules/postman-request": { + "version": "2.88.1-postman.48", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.48.tgz", + "integrity": "sha512-E32FGh8ig2KDvzo4Byi7Ibr+wK2gNKPSqXoNsvjdCHgDBxSK4sCUwv+aa3zOBUwfiibPImHMy0WdlDSSCTqTuw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@postman/form-data": "~3.1.1", + "@postman/tough-cookie": "~4.1.3-postman.1", + "@postman/tunnel-agent": "^0.6.8", + "aws-sign2": "~0.7.0", + "aws4": "^1.12.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "http-signature": "~1.4.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "^2.1.35", + "oauth-sign": "~0.9.0", + "qs": "~6.14.1", + "safe-buffer": "^5.1.2", + "socks-proxy-agent": "^8.0.5", + "stream-length": "^1.0.2", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 16" + } + }, "node_modules/newman/node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -9051,6 +9317,14 @@ "dev": true, "license": "MIT" }, + "node_modules/node-bitmap": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/node-bitmap/-/node-bitmap-0.0.1.tgz", + "integrity": "sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==", + "engines": { + "node": ">=v0.6.5" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -9108,9 +9382,9 @@ } }, "node_modules/nodemon/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -9181,6 +9455,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9225,9 +9500,9 @@ } }, "node_modules/npm-run-all/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -9395,6 +9670,15 @@ "node": "*" } }, + "node_modules/obj-extend": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/obj-extend/-/obj-extend-0.1.0.tgz", + "integrity": "sha512-or9c7Ue2wWCun41DuLP3+LKEUjSZcDSxfCM4HZQSX9tcjLL/yuzTW7MmtVNs+MmN16uDRpDrFmFK/WVSm4vklg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -9517,6 +9801,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "license": "MIT" + }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", @@ -9587,9 +9877,9 @@ } }, "node_modules/ora": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-9.0.0.tgz", - "integrity": "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-9.1.0.tgz", + "integrity": "sha512-53uuLsXHOAJl5zLrUrzY9/kE+uIFEx7iaH4g2BIJQK4LZjY4LpCCYZVKDWIkL+F01wAaCg93duQ1whnK/AmY1A==", "dev": true, "license": "MIT", "dependencies": { @@ -9600,8 +9890,7 @@ "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.2.2", - "string-width": "^8.1.0", - "strip-ansi": "^7.1.2" + "string-width": "^8.1.0" }, "engines": { "node": ">=20" @@ -9716,12 +10005,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true - }, "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", @@ -9739,6 +10022,15 @@ "node": ">=6" } }, + "node_modules/parse-data-uri": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/parse-data-uri/-/parse-data-uri-0.2.0.tgz", + "integrity": "sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==", + "license": "ISC", + "dependencies": { + "data-uri-to-buffer": "0.0.3" + } + }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -9794,22 +10086,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", @@ -9819,6 +10095,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "engines": { "node": ">=8" } @@ -9921,20 +10198,20 @@ } }, "node_modules/pino-pretty": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.2.tgz", - "integrity": "sha512-3cN0tCakkT4f3zo9RXDIhy6GTvtYD6bK4CRBLN9j3E/ePqN1tugAXD5rGVfoChW6s0hiek+eyYlLNqc/BG7vBQ==", + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz", + "integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==", "license": "MIT", "dependencies": { "colorette": "^2.0.7", "dateformat": "^4.6.3", - "fast-copy": "^3.0.2", + "fast-copy": "^4.0.0", "fast-safe-stringify": "^2.1.1", "help-me": "^5.0.0", "joycon": "^3.1.1", "minimist": "^1.2.6", "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^2.0.0", + "pino-abstract-transport": "^3.0.0", "pump": "^3.0.0", "secure-json-parse": "^4.0.0", "sonic-boom": "^4.0.1", @@ -9944,6 +10221,15 @@ "pino-pretty": "bin.js" } }, + "node_modules/pino-pretty/node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, "node_modules/pino-pretty/node_modules/strip-json-comments": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", @@ -9970,6 +10256,64 @@ "node": ">= 6" } }, + "node_modules/pixelsmith": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/pixelsmith/-/pixelsmith-2.6.0.tgz", + "integrity": "sha512-1W0C8EVxAPJwsCodw/+dfeEtdSc8JuHFipVylf51PIvh7S7Q33qmVCCzeWQp1y1sXpZ52iXGY2D/ICMyHPIULw==", + "dependencies": { + "async": "^3.2.3", + "concat-stream": "~1.5.1", + "get-pixels": "~3.3.0", + "mime-types": "~2.1.7", + "ndarray": "~1.0.15", + "obj-extend": "~0.1.0", + "save-pixels": "~2.3.0", + "vinyl-file": "~1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/pixelsmith/node_modules/concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha512-H6xsIBfQ94aESBG8jGHXQ7i5AEpy5ZeVaLDOisDICiTCKpqEfr34/KmTrspKQNoLKNu9gTkovlpQcUi630AKiQ==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "~2.0.0", + "typedarray": "~0.0.5" + } + }, + "node_modules/pixelsmith/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/pixelsmith/node_modules/readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha512-TXcFfb63BQe1+ySzsHZI/5v1aJPCShfqvWJ64ayNImXMsN1Cd0YGk/wm8KB7/OeessgPc9QvS9Zou8QTkFzsLw==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/pixelsmith/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, "node_modules/pjax": { "version": "0.2.8", "resolved": "https://registry.npmjs.org/pjax/-/pjax-0.2.8.tgz", @@ -9988,6 +10332,25 @@ "pathe": "^2.0.1" } }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, + "node_modules/pngjs-nozlib": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pngjs-nozlib/-/pngjs-nozlib-1.0.0.tgz", + "integrity": "sha512-N1PggqLp9xDqwAoKvGohmZ3m4/N9xpY0nDZivFqQLcpLHmliHnCp9BuNCsOeqHWMuEEgFjpEaq9dZq6RZyy0fA==", + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -10498,6 +10861,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==", + "license": "MIT" + }, "node_modules/process-warning": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", @@ -10616,11 +10985,12 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -10639,6 +11009,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -10675,25 +11046,40 @@ "node": ">= 0.6" } }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", - "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", - "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.0" + "react": "^19.2.4" } }, "node_modules/react-is": { @@ -10755,6 +11141,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -10766,6 +11153,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -10839,6 +11227,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -10920,6 +11316,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -10968,6 +11365,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -11082,6 +11480,21 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/save-pixels": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/save-pixels/-/save-pixels-2.3.6.tgz", + "integrity": "sha512-/ayfEWBxt0tFpf5lxSU1S0+/TBn7EiaTZD+6GL+mwizHm3BKCBysnzT6Js7BusDUVcNVLkeJJKLZcBgdpM2leQ==", + "license": "MIT", + "dependencies": { + "contentstream": "^1.0.0", + "gif-encoder": "~0.4.1", + "jpeg-js": "^0.4.3", + "ndarray": "^1.0.18", + "ndarray-ops": "^1.2.2", + "pngjs-nozlib": "^1.0.0", + "through": "^2.3.4" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -11117,23 +11530,24 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~2.0.2" }, "engines": { "node": ">= 0.8.0" @@ -11152,10 +11566,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "node_modules/send/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -11171,25 +11586,16 @@ "uuid": "^3.0.0" } }, - "node_modules/serialised-error/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.19.0" + "send": "~0.19.1" }, "engines": { "node": ">= 0.8.0" @@ -11384,6 +11790,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "engines": { "node": ">=8" } @@ -11405,6 +11812,47 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/sonic-boom": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", @@ -11417,7 +11865,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -11475,36 +11922,101 @@ "node": ">= 10.x" } }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, + "node_modules/spritesmith": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/spritesmith/-/spritesmith-3.5.1.tgz", + "integrity": "sha512-FrzGx9cD5SkH3Kxuk24vJJLBYHLyDskUJ2t6DSWfWkCd8+yFQAfBGVE7F0IhOpKu+aq1CZ9rgXnpaa4luIQgGg==", + "license": "MIT", "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" + "concat-stream": "~1.5.1", + "layout": "~2.2.0", + "pixelsmith": "^2.3.0", + "semver": "~5.7.2", + "through2": "~2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 4.0.0" } }, - "node_modules/sshpk/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true + "node_modules/spritesmith/node_modules/concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha512-H6xsIBfQ94aESBG8jGHXQ7i5AEpy5ZeVaLDOisDICiTCKpqEfr34/KmTrspKQNoLKNu9gTkovlpQcUi630AKiQ==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "~2.0.0", + "typedarray": "~0.0.5" + } + }, + "node_modules/spritesmith/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/spritesmith/node_modules/readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha512-TXcFfb63BQe1+ySzsHZI/5v1aJPCShfqvWJ64ayNImXMsN1Cd0YGk/wm8KB7/OeessgPc9QvS9Zou8QTkFzsLw==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/spritesmith/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/spritesmith/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true }, "node_modules/stable-hash-x": { "version": "0.2.0", @@ -11603,21 +12115,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", @@ -11741,19 +12238,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -11763,6 +12247,31 @@ "node": ">=4" } }, + "node_modules/strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha512-7jfJB9YpI2Z0aH3wu10ZqitvYJaE0s5IzFuWE+0pbb4Q/armTloEUShymkDO47YSLnjAW52mlXT//hs9wXNNJQ==", + "license": "MIT", + "dependencies": { + "first-chunk-stream": "^1.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom-stream/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "license": "MIT", + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -11788,17 +12297,18 @@ "license": "MIT" }, "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", - "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { @@ -11819,20 +12329,20 @@ } }, "node_modules/superagent": { - "version": "10.2.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", - "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", + "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", "license": "MIT", "dependencies": { "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.4", + "form-data": "^4.0.5", "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.2" + "qs": "^6.14.1" }, "engines": { "node": ">=14.18.0" @@ -11851,18 +12361,28 @@ } }, "node_modules/supertest": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.4.tgz", - "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", + "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", "license": "MIT", "dependencies": { + "cookie-signature": "^1.2.2", "methods": "^1.1.2", - "superagent": "^10.2.3" + "superagent": "^10.3.0" }, "engines": { "node": ">=14.18.0" } }, + "node_modules/supertest/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11977,6 +12497,55 @@ "real-require": "^0.2.0" } }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/through2/node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", @@ -11984,14 +12553,14 @@ "dev": true }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -12004,6 +12573,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -12080,527 +12650,103 @@ "dev": true, "license": "Apache-2.0", "dependencies": { - "dprint-node": "^1.0.8" - } - }, - "node_modules/ts-proto": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-2.8.3.tgz", - "integrity": "sha512-TdXInqG+61pj/TvORqITWjvjTTsL1EZxwX49iEj89+xFAcqPT8tjChpAGQXzfcF4MJwvNiuoCEbBOKqVf3ds3g==", - "dev": true, - "license": "ISC", - "dependencies": { - "@bufbuild/protobuf": "^2.0.0", - "case-anything": "^2.1.13", - "ts-poet": "^6.12.0", - "ts-proto-descriptors": "2.0.0" - }, - "bin": { - "protoc-gen-ts_proto": "protoc-gen-ts_proto" - } - }, - "node_modules/ts-proto-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-2.0.0.tgz", - "integrity": "sha512-wHcTH3xIv11jxgkX5OyCSFfw27agpInAd6yh89hKG6zqIXnjW9SYqSER2CVQxdPj4czeOhGagNvZBEbJPy7qkw==", - "dev": true, - "dependencies": { - "@bufbuild/protobuf": "^2.0.0" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "node_modules/tsup": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", - "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", - "dev": true, - "license": "MIT", - "dependencies": { - "bundle-require": "^5.1.0", - "cac": "^6.7.14", - "chokidar": "^4.0.3", - "consola": "^3.4.0", - "debug": "^4.4.0", - "esbuild": "^0.27.0", - "fix-dts-default-cjs-exports": "^1.0.0", - "joycon": "^3.1.1", - "picocolors": "^1.1.1", - "postcss-load-config": "^6.0.1", - "resolve-from": "^5.0.0", - "rollup": "^4.34.8", - "source-map": "^0.7.6", - "sucrase": "^3.35.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.11", - "tree-kill": "^1.2.2" - }, - "bin": { - "tsup": "dist/cli-default.js", - "tsup-node": "dist/cli-node.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@microsoft/api-extractor": "^7.36.0", - "@swc/core": "^1", - "postcss": "^8.4.12", - "typescript": ">=4.5.0" - }, - "peerDependenciesMeta": { - "@microsoft/api-extractor": { - "optional": true - }, - "@swc/core": { - "optional": true - }, - "postcss": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/tsup/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", - "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/android-arm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", - "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/android-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", - "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/android-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", - "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", - "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/darwin-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", - "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", - "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", - "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-arm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", - "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", - "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-ia32": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", - "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-loong64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", - "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", - "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", - "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", - "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-s390x": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", - "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", - "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", - "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", - "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", - "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", - "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/sunos-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", - "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", - "cpu": [ - "x64" - ], + "dprint-node": "^1.0.8" + } + }, + "node_modules/ts-proto": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-2.11.2.tgz", + "integrity": "sha512-+bPq2zDwNtJHtuyu2fGYgvTDxS7cBcY4u4moO1LyFA+my144Mfj9/sbNyGXKMtBK7DNXVGf76puwGa6hPzJw/g==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" + "license": "ISC", + "dependencies": { + "@bufbuild/protobuf": "^2.10.2", + "case-anything": "^2.1.13", + "ts-poet": "^6.12.0", + "ts-proto-descriptors": "2.1.0" + }, + "bin": { + "protoc-gen-ts_proto": "protoc-gen-ts_proto" } }, - "node_modules/tsup/node_modules/@esbuild/win32-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", - "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", - "cpu": [ - "arm64" - ], + "node_modules/ts-proto-descriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-2.1.0.tgz", + "integrity": "sha512-S5EZYEQ6L9KLFfjSRpZWDIXDV/W7tAj8uW7pLsihIxyr62EAVSiKuVPwE8iWnr849Bqa53enex1jhDUcpgquzA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "license": "ISC", + "dependencies": { + "@bufbuild/protobuf": "^2.0.0" } }, - "node_modules/tsup/node_modules/@esbuild/win32-ia32": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", - "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", - "cpu": [ - "ia32" - ], + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" } }, - "node_modules/tsup/node_modules/@esbuild/win32-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", - "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", - "cpu": [ - "x64" - ], + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } } }, "node_modules/tsup/node_modules/chokidar": { @@ -12618,48 +12764,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/tsup/node_modules/esbuild": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", - "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.0", - "@esbuild/android-arm": "0.27.0", - "@esbuild/android-arm64": "0.27.0", - "@esbuild/android-x64": "0.27.0", - "@esbuild/darwin-arm64": "0.27.0", - "@esbuild/darwin-x64": "0.27.0", - "@esbuild/freebsd-arm64": "0.27.0", - "@esbuild/freebsd-x64": "0.27.0", - "@esbuild/linux-arm": "0.27.0", - "@esbuild/linux-arm64": "0.27.0", - "@esbuild/linux-ia32": "0.27.0", - "@esbuild/linux-loong64": "0.27.0", - "@esbuild/linux-mips64el": "0.27.0", - "@esbuild/linux-ppc64": "0.27.0", - "@esbuild/linux-riscv64": "0.27.0", - "@esbuild/linux-s390x": "0.27.0", - "@esbuild/linux-x64": "0.27.0", - "@esbuild/netbsd-arm64": "0.27.0", - "@esbuild/netbsd-x64": "0.27.0", - "@esbuild/openbsd-arm64": "0.27.0", - "@esbuild/openbsd-x64": "0.27.0", - "@esbuild/openharmony-arm64": "0.27.0", - "@esbuild/sunos-x64": "0.27.0", - "@esbuild/win32-arm64": "0.27.0", - "@esbuild/win32-ia32": "0.27.0", - "@esbuild/win32-x64": "0.27.0" - } - }, "node_modules/tsup/node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -12693,12 +12797,12 @@ } }, "node_modules/tsx": { - "version": "4.20.6", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", - "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "license": "MIT", "dependencies": { - "esbuild": "~0.25.0", + "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "bin": { @@ -12869,7 +12973,6 @@ "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -12925,10 +13028,17 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "license": "MIT" + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, "engines": { "node": ">= 10.0.0" } @@ -13047,6 +13157,17 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/uvm": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/uvm/-/uvm-2.1.1.tgz", @@ -13104,6 +13225,53 @@ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, + "node_modules/vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha512-Ci3wnR2uuSAWFMSglZuB8Z2apBdtOyz8CV7dC6/U1XbltXBC+IuutUkXQISz01P+US2ouBuesSbV6zILZ6BuzQ==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + }, + "engines": { + "node": ">= 0.9" + } + }, + "node_modules/vinyl-file": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-1.3.0.tgz", + "integrity": "sha512-i1CGRaiDs3qJ+Yc8cgtOnrZOwlhY02oDBrWSBKD9uYSsxqQG1RhNXLmR/orke0ye0sbKpVtAUHwhF2rs9A46cQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "strip-bom": "^2.0.0", + "strip-bom-stream": "^1.0.0", + "vinyl": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl-file/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "license": "MIT", + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -13244,105 +13412,7 @@ "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, "node_modules/wrappy": { "version": "1.0.2", @@ -13446,14 +13516,43 @@ } }, "node_modules/zod": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", - "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, + "packages/esbuild-plugin-oxipng": { + "name": "@repo/esbuild-plugin-oxipng", + "version": "0.0.0", + "hasInstallScript": true, + "dependencies": { + "@jsquash/oxipng": "^2.3.0", + "esbuild": "^0.27.2" + }, + "devDependencies": { + "@types/node": "^22.19.1", + "typescript": "^5.9.3" + } + }, + "packages/esbuild-plugin-spritesmith": { + "name": "@repo/esbuild-plugin-spritesmith", + "version": "0.0.0", + "hasInstallScript": true, + "dependencies": { + "esbuild": "^0.27.2", + "handlebars": "^4.7.8", + "pixelsmith": "^2.6.0", + "spritesmith": "^3.5.1" + }, + "devDependencies": { + "@types/node": "^22.19.1", + "@types/spritesmith": "^3.4.5", + "typescript": "^5.9.3" + } + }, "packages/grpc-client": { "name": "@repo/grpc-client", "version": "0.0.0", @@ -13461,7 +13560,7 @@ "devDependencies": { "@bufbuild/buf": "^1.61.0", "nice-grpc-common": "^2.0.2", - "ts-proto": "^2.8.3", + "ts-proto": "^2.11.2", "typescript": "^5.9.3" } } diff --git a/package.json b/package.json index 80a08f88..589c7805 100644 --- a/package.json +++ b/package.json @@ -6,5 +6,8 @@ ], "devDependencies": { "@bufbuild/buf": "^1.61.0" + }, + "overrides": { + "get-pixels": "github:scijs/get-pixels#5283976" } } diff --git a/packages/esbuild-plugin-oxipng/package.json b/packages/esbuild-plugin-oxipng/package.json new file mode 100644 index 00000000..c410f422 --- /dev/null +++ b/packages/esbuild-plugin-oxipng/package.json @@ -0,0 +1,21 @@ +{ + "name": "@repo/esbuild-plugin-oxipng", + "private": true, + "version": "0.0.0", + "description": "ESBuild plugin to optimise PNG images", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "preinstall": "npm run build" + }, + "dependencies": { + "@jsquash/oxipng": "^2.3.0", + "esbuild": "^0.27.2" + }, + "devDependencies": { + "@types/node": "^22.19.1", + "typescript": "^5.9.3" + } +} diff --git a/packages/esbuild-plugin-oxipng/src/index.ts b/packages/esbuild-plugin-oxipng/src/index.ts new file mode 100644 index 00000000..209a3404 --- /dev/null +++ b/packages/esbuild-plugin-oxipng/src/index.ts @@ -0,0 +1,48 @@ +import { Loader, OnLoadArgs, OnLoadResult, OnStartResult, Plugin, PluginBuild } from 'esbuild'; +import { readFile } from 'node:fs/promises'; +// This is really what they recommend doing https://github.com/jamsinclair/jSquash/blob/main/examples/with-node/index.js +import { default as optimiseOxi, init as initOxi } from '@jsquash/oxipng/optimise.js'; +import { basename } from 'node:path'; +import { OptimiseOptions } from '@jsquash/oxipng/meta'; + +const NAMESPACE = 'esbuild-plugin-oxipng'; +const FILTER = /.png$/; + +export type Options = { + loader?: Loader, + opts?: Partial +} + +export function oxipng(options?: Options): Plugin { + const loader = options?.loader ?? 'file'; + const oxiOpts = options?.opts ?? { level: 3, optimiseAlpha: true }; + + async function onLoad(args: OnLoadArgs): Promise { + const data = await readFile(args.path); + const optimised = await optimiseOxi(data.buffer, oxiOpts); + console.log(`OXI ${basename(args.path)}: ${data.length}b -> ${optimised.byteLength}b`) + + return { + contents: new Uint8Array(optimised), + loader: loader, + pluginName: NAMESPACE, + } + } + + return { + name: NAMESPACE, + setup(build: PluginBuild) { + build.onLoad({ + filter: FILTER, + namespace: 'file', + }, onLoad) + } + } +} + +async function init() { + const wasmPath = new URL(import.meta.resolve('@jsquash/oxipng/codec/pkg/squoosh_oxipng_bg.wasm')); + const wasm = await readFile(wasmPath); + await initOxi(wasm); +} +await init(); diff --git a/packages/esbuild-plugin-oxipng/tsconfig.json b/packages/esbuild-plugin-oxipng/tsconfig.json new file mode 100644 index 00000000..a17d849e --- /dev/null +++ b/packages/esbuild-plugin-oxipng/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "skipLibCheck": true, + "declaration": true, + "module": "ES2022", + "target": "es2022", + "outDir": "dist", + "strict": true, + "noEmitOnError": true, + "esModuleInterop": true, + }, + "include": [ + "src" + ], + "exclude": [] +} \ No newline at end of file diff --git a/packages/esbuild-plugin-spritesmith/package.json b/packages/esbuild-plugin-spritesmith/package.json new file mode 100644 index 00000000..2542e7ca --- /dev/null +++ b/packages/esbuild-plugin-spritesmith/package.json @@ -0,0 +1,24 @@ +{ + "name": "@repo/esbuild-plugin-spritesmith", + "private": true, + "version": "0.0.0", + "description": "ESBuild plugin to create spritesheets", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "preinstall": "npm run build" + }, + "dependencies": { + "esbuild": "^0.27.2", + "handlebars": "^4.7.8", + "pixelsmith": "^2.6.0", + "spritesmith": "^3.5.1" + }, + "devDependencies": { + "@types/node": "^22.19.1", + "@types/spritesmith": "^3.4.5", + "typescript": "^5.9.3" + } +} diff --git a/packages/esbuild-plugin-spritesmith/src/index.ts b/packages/esbuild-plugin-spritesmith/src/index.ts new file mode 100644 index 00000000..39922de7 --- /dev/null +++ b/packages/esbuild-plugin-spritesmith/src/index.ts @@ -0,0 +1,65 @@ +import { OnStartResult, Plugin, PluginBuild } from 'esbuild'; +import { readdir, writeFile } from 'node:fs/promises'; +import { basename } from 'node:path'; +import { promisify } from 'node:util'; +import Spritesmith from 'spritesmith'; +import Handlebars from 'handlebars'; + +const NAMESPACE = 'esbuild-plugin-spritesmith'; + +export type Options = { + input_folder: string, + output_css: string, + output_image: string, + output_image_url: string, +} + +export function spritesmith(options: Options): Plugin { + const { input_folder, output_css, output_image, output_image_url } = options; + + return { + name: NAMESPACE, + setup(build: PluginBuild) { + build.onStart(async (): Promise => { + const sprite_paths = (await readdir(input_folder)) + .filter(path => path.endsWith('.png')) + .map(path => input_folder + path); + + + const { coordinates, image } = await promisify(Spritesmith.run)({ + src: sprite_paths + }); + + const sprites = Object.entries(coordinates).map(([name, coords]) => { + return { + name: basename(name, '.png').replaceAll('_', ':'), + ...coords + }; + }); + + // make the css + const css = Handlebars.compile(` + /* This file is generated by @repo/esbuild-plugin-spritesmith */ + .sprite { + background-image: url(${output_image_url}); + } + {{#each sprites}} + .sprite.sp-{{name}} { + background-position: -{{x}}px -{{y}}px; + width: {{width}}px; + height: {{height}}px; + } + {{/each}} + `)({ sprites }); + + + await writeFile(output_css, css, { + encoding: 'utf-8' + }); + await writeFile(output_image, image); + console.info(`SMTH: wrote ${sprite_paths.length} sprites`); + return {}; + }); + } + } +} diff --git a/packages/esbuild-plugin-spritesmith/tsconfig.json b/packages/esbuild-plugin-spritesmith/tsconfig.json new file mode 100644 index 00000000..a17d849e --- /dev/null +++ b/packages/esbuild-plugin-spritesmith/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "skipLibCheck": true, + "declaration": true, + "module": "ES2022", + "target": "es2022", + "outDir": "dist", + "strict": true, + "noEmitOnError": true, + "esModuleInterop": true, + }, + "include": [ + "src" + ], + "exclude": [] +} \ No newline at end of file diff --git a/packages/grpc-client/package.json b/packages/grpc-client/package.json index 349c455c..af0c0cfb 100644 --- a/packages/grpc-client/package.json +++ b/packages/grpc-client/package.json @@ -14,7 +14,7 @@ "devDependencies": { "@bufbuild/buf": "^1.61.0", "nice-grpc-common": "^2.0.2", - "ts-proto": "^2.8.3", + "ts-proto": "^2.11.2", "typescript": "^5.9.3" }, "exports": {