mirror of
https://github.com/PreMiD/PreMiD.git
synced 2026-04-06 04:41:58 +02:00
Compare commits
19 Commits
api-worker
...
api-worker
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a71b66540b | ||
|
|
e675f74983 | ||
|
|
e9e6639492 | ||
|
|
3258179040 | ||
|
|
086d476af2 | ||
|
|
146bf9e270 | ||
|
|
a02f25ba29 | ||
|
|
416b65f0d4 | ||
|
|
f8e9fc832d | ||
|
|
86b0f07216 | ||
|
|
9eb5c03877 | ||
|
|
e63e1270aa | ||
|
|
f730e71bbf | ||
|
|
8b68bf85c8 | ||
|
|
e4c794a9ad | ||
|
|
6e8258d76f | ||
|
|
56b796c621 | ||
|
|
0de59c48b4 | ||
|
|
60056e069d |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -3,6 +3,7 @@ out
|
||||
dist
|
||||
tmp
|
||||
lib
|
||||
data
|
||||
|
||||
.vscode
|
||||
.env
|
||||
@@ -22,4 +23,5 @@ src/update.ini
|
||||
!eslint.config.js
|
||||
|
||||
coverage
|
||||
*.tsbuildinfo
|
||||
*.tsbuildinfo
|
||||
.DS_Store
|
||||
10
apps/api-master/environment.d.ts
vendored
Normal file
10
apps/api-master/environment.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
declare module "ip-location-api" {
|
||||
export function lookup(ip: string): Promise<{
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
country: string;
|
||||
} | null>;
|
||||
|
||||
export function updateDb(options: { fields?: string[]; dataDir?: string; tmpDataDir?: string }): Promise<void>;
|
||||
export function reload(options: { fields?: string[]; dataDir?: string; tmpDataDir?: string }): Promise<void>;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@premid/api-master",
|
||||
"type": "module",
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.24",
|
||||
"private": true,
|
||||
"description": "PreMiD's api master",
|
||||
"license": "MPL-2.0",
|
||||
@@ -14,7 +14,6 @@
|
||||
"dev": "node --watch --env-file .env --enable-source-maps ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordjs/rest": "^2.3.0",
|
||||
"@envelop/sentry": "^9.0.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/exporter-prometheus": "^0.52.1",
|
||||
@@ -23,6 +22,8 @@
|
||||
"cron": "^3.1.7",
|
||||
"debug": "^4.3.6",
|
||||
"ioredis": "^5.3.2",
|
||||
"ip-location-api": "^1.0.0",
|
||||
"ky": "^1.7.2",
|
||||
"p-limit": "^6.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { REST } from "@discordjs/rest";
|
||||
import pLimit from "p-limit";
|
||||
import ky, { HTTPError, TimeoutError } from "ky";
|
||||
import { mainLog, redis } from "../index.js";
|
||||
|
||||
let inProgress = false;
|
||||
@@ -11,6 +11,7 @@ export async function clearOldSessions() {
|
||||
|
||||
inProgress = true;
|
||||
const now = Date.now();
|
||||
const pattern = "pmd-api.sessions.*";
|
||||
let cursor = "0";
|
||||
let totalSessions = 0;
|
||||
let cleared = 0;
|
||||
@@ -22,21 +23,15 @@ export async function clearOldSessions() {
|
||||
const limit = pLimit(100); // Create a limit of 100 concurrent operations
|
||||
|
||||
do {
|
||||
const [nextCursor, result] = await redis.hscan("pmd-api.sessions", cursor, "COUNT", batchSize);
|
||||
cursor = nextCursor;
|
||||
totalSessions += result.length / 2;
|
||||
const [newCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", 1000); //* Use SCAN with COUNT for memory efficiency
|
||||
|
||||
const deletePromises = [];
|
||||
cursor = newCursor;
|
||||
totalSessions += keys.length;
|
||||
|
||||
for (let i = 0; i < result.length; i += 2) {
|
||||
const key = result[i];
|
||||
const value = result[i + 1];
|
||||
const deletePromises: Promise<string>[] = [];
|
||||
|
||||
if (!key || !value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const session = JSON.parse(value) as {
|
||||
for (const key of keys) {
|
||||
const session = await redis.hgetall(key) as unknown as {
|
||||
token: string;
|
||||
session: string;
|
||||
lastUpdated: number;
|
||||
@@ -57,13 +52,13 @@ export async function clearOldSessions() {
|
||||
});
|
||||
|
||||
if (keysToDelete.length >= batchSize) {
|
||||
await redis.hdel("pmd-api.sessions", ...keysToDelete);
|
||||
await redis.del(...keysToDelete);
|
||||
keysToDelete = [];
|
||||
}
|
||||
} while (cursor !== "0");
|
||||
|
||||
if (keysToDelete.length > 0) {
|
||||
await redis.hdel("pmd-api.sessions", ...keysToDelete);
|
||||
await redis.del(...keysToDelete);
|
||||
}
|
||||
|
||||
if (totalSessions === 0) {
|
||||
@@ -77,41 +72,29 @@ export async function clearOldSessions() {
|
||||
}
|
||||
|
||||
async function deleteSession(session: { token: string; session: string }, key: string): Promise<string> {
|
||||
const abortController = new AbortController();
|
||||
const timeoutId = setTimeout(() => abortController.abort("Timeout"), 5000); //* 5 second timeout
|
||||
|
||||
try {
|
||||
const discord = new REST({ version: "10", authPrefix: "Bearer" });
|
||||
discord.setToken(session.token);
|
||||
|
||||
await discord.post("/users/@me/headless-sessions/delete", {
|
||||
signal: abortController.signal,
|
||||
body: {
|
||||
await ky.post("https://discord.com/api/v10/users/@me/headless-sessions/delete", {
|
||||
json: {
|
||||
token: session.session,
|
||||
},
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.token}`,
|
||||
},
|
||||
retry: 3,
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
return key;
|
||||
}
|
||||
catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
//* Log detailed error information
|
||||
mainLog(`Delete session error for key ${key}:`, {
|
||||
errorName: error instanceof Error ? error.name : "Unknown",
|
||||
errorMessage: error instanceof Error ? error.message : String(error),
|
||||
errorStack: error instanceof Error ? error.stack : "No stack trace",
|
||||
});
|
||||
|
||||
if (error instanceof Error && error.name === "AbortError") {
|
||||
if (error instanceof TimeoutError) {
|
||||
mainLog(`Session deletion aborted due to timeout for key ${key}`);
|
||||
}
|
||||
else if (error instanceof Error) {
|
||||
mainLog(`Failed to delete session for key ${key}: ${error.message}`);
|
||||
else if (error instanceof HTTPError) {
|
||||
mainLog(`Failed to delete session for key ${key}: [${error.name}] ${error.message} ${JSON.stringify(await error.response.json())}`);
|
||||
}
|
||||
else {
|
||||
mainLog(`Failed to delete session for key ${key}: Unknown error`);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
83
apps/api-master/src/functions/clearableGaugeMetric.ts
Normal file
83
apps/api-master/src/functions/clearableGaugeMetric.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import type { ServerResponse } from "node:http";
|
||||
import type { Attributes } from "@opentelemetry/api";
|
||||
import { ValueType, diag } from "@opentelemetry/api";
|
||||
import type { PrometheusExporter, PrometheusSerializer } from "@opentelemetry/exporter-prometheus";
|
||||
import { AggregationTemporality, DataPointType, type GaugeMetricData, InstrumentType } from "@opentelemetry/sdk-metrics";
|
||||
|
||||
const registeredMetrics = new Map<string, ClearableGaugeMetric>();
|
||||
|
||||
//* Custom gauge metric class
|
||||
export class ClearableGaugeMetric {
|
||||
private data: Map<string, { value: number; attributes: Attributes }>;
|
||||
private name: string;
|
||||
private description: string;
|
||||
|
||||
constructor(name: string, description: string) {
|
||||
this.data = new Map();
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
registeredMetrics.set(name, this);
|
||||
}
|
||||
|
||||
set(key: string, value: number, attributes: Attributes) {
|
||||
this.data.set(key, { value, attributes });
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.data.clear();
|
||||
}
|
||||
|
||||
toMetricData(): GaugeMetricData {
|
||||
return {
|
||||
descriptor: {
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
unit: "",
|
||||
type: InstrumentType.GAUGE,
|
||||
valueType: ValueType.INT,
|
||||
},
|
||||
dataPointType: DataPointType.GAUGE,
|
||||
dataPoints: Array.from(this.data.values()).map(({ value, attributes }) => ({
|
||||
value,
|
||||
attributes,
|
||||
startTime: [0, 0],
|
||||
endTime: [0, 0],
|
||||
})),
|
||||
aggregationTemporality: AggregationTemporality.CUMULATIVE,
|
||||
};
|
||||
}
|
||||
|
||||
get hasData() {
|
||||
return this.data.size > 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function updatePrometheusMetrics(prometheusExporter: PrometheusExporter) {
|
||||
// @ts-expect-error We are modifying a private method
|
||||
prometheusExporter._exportMetrics = function (this: PrometheusExporter, response: ServerResponse) {
|
||||
response.statusCode = 200;
|
||||
response.setHeader("content-type", "text/plain");
|
||||
this.collect().then(
|
||||
(collectionResult) => {
|
||||
const { resourceMetrics, errors } = collectionResult;
|
||||
if (errors.length) {
|
||||
diag.error(
|
||||
"PrometheusExporter: metrics collection errors",
|
||||
...errors,
|
||||
);
|
||||
}
|
||||
|
||||
for (const metric of registeredMetrics.values()) {
|
||||
if (metric.hasData) {
|
||||
resourceMetrics.scopeMetrics[0]!.metrics.push(metric.toMetricData());
|
||||
}
|
||||
}
|
||||
|
||||
response.end((this as unknown as { _serializer: PrometheusSerializer })._serializer.serialize(resourceMetrics));
|
||||
},
|
||||
(err) => {
|
||||
response.end(`# failed to export metrics: ${err}`);
|
||||
},
|
||||
);
|
||||
}.bind(prometheusExporter);
|
||||
}
|
||||
46
apps/api-master/src/functions/lookupIp.ts
Normal file
46
apps/api-master/src/functions/lookupIp.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { join } from "node:path";
|
||||
import process from "node:process";
|
||||
import { lookup, reload, updateDb } from "ip-location-api";
|
||||
import { mainLog } from "../index.js";
|
||||
|
||||
const fields = ["latitude", "longitude", "country"];
|
||||
|
||||
const dataDir = join(process.cwd(), "data");
|
||||
const tmpDataDir = join(process.cwd(), "tmp");
|
||||
|
||||
let initialized = false;
|
||||
|
||||
export async function lookupIp(ip: string): Promise<{ latitude: number; longitude: number; country: string } | undefined> {
|
||||
if (!initialized) {
|
||||
reloadIpLocationApi();
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return await lookup(ip) ?? undefined;
|
||||
}
|
||||
catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
let reloading: Promise<void> | undefined;
|
||||
let log: debug.Debugger | undefined;
|
||||
|
||||
export async function reloadIpLocationApi() {
|
||||
log ??= mainLog.extend("IP-Location-API");
|
||||
|
||||
if (reloading)
|
||||
return reloading;
|
||||
|
||||
reloading = new Promise((resolve, reject) => {
|
||||
log?.("Reloading IP location API");
|
||||
updateDb({ fields, dataDir, tmpDataDir }).then(async () => {
|
||||
await reload({ fields, dataDir, tmpDataDir });
|
||||
log?.("IP location API reloaded");
|
||||
initialized = true;
|
||||
reloading = undefined;
|
||||
resolve();
|
||||
}).catch(reject);
|
||||
});
|
||||
return reloading;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import { redis } from "../index.js";
|
||||
import { counter } from "../tracing.js";
|
||||
import { activeSessionsCounter } from "../tracing.js";
|
||||
|
||||
let activeActivities = 0;
|
||||
counter.add(0);
|
||||
export async function setCounter() {
|
||||
activeSessionsCounter.add(0);
|
||||
export async function setSessionCounter() {
|
||||
const length = await redis.hlen("pmd-api.sessions");
|
||||
if (length === activeActivities)
|
||||
return;
|
||||
const diff = length - activeActivities;
|
||||
activeActivities = length;
|
||||
counter.add(diff);
|
||||
activeSessionsCounter.add(diff);
|
||||
}
|
||||
64
apps/api-master/src/functions/updateActivePresenceGauge.ts
Normal file
64
apps/api-master/src/functions/updateActivePresenceGauge.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { redis } from "../index.js";
|
||||
import { activeIpsGauge, activePresenceGauge } from "../tracing.js";
|
||||
import { lookupIp } from "./lookupIp.js";
|
||||
|
||||
//* Function to update the gauge with per-service counts
|
||||
export async function updateActivePresenceGauge() {
|
||||
const pattern = "pmd-api.heartbeatUpdates.*";
|
||||
let cursor: string = "0";
|
||||
const serviceCounts = new Map<string, number>();
|
||||
const ips = new Map<string, {
|
||||
presences: string[];
|
||||
sessions: number;
|
||||
}>();
|
||||
|
||||
do {
|
||||
const [newCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", 1000); //* Use SCAN with COUNT for memory efficiency
|
||||
cursor = newCursor;
|
||||
for (const key of keys) {
|
||||
const hash = await redis.hgetall(key);
|
||||
const service = hash.service;
|
||||
const version = hash.version; //* Get version from hash
|
||||
const ip = hash.ip_address;
|
||||
if (service && version) {
|
||||
serviceCounts.set(`${service}:${version}`, (serviceCounts.get(`${service}:${version}`) || 0) + 1);
|
||||
}
|
||||
else {
|
||||
serviceCounts.set("none", (serviceCounts.get("none") || 0) + 1);
|
||||
}
|
||||
if (ip) {
|
||||
const presenceName = service && version ? `${service}:${version}` : undefined;
|
||||
const ipData = ips.get(ip) || { presences: [], sessions: 0 };
|
||||
ipData.presences = [...new Set<string>([...ipData.presences, presenceName].filter(Boolean) as string[])];
|
||||
ipData.sessions++;
|
||||
ips.set(ip, ipData);
|
||||
}
|
||||
}
|
||||
} while (cursor !== "0");
|
||||
|
||||
// Clear previous data
|
||||
activePresenceGauge.clear();
|
||||
activeIpsGauge.clear();
|
||||
|
||||
// Set new data
|
||||
for (const [serviceVersion, count] of serviceCounts.entries()) {
|
||||
const [presence_name, version] = serviceVersion.split(":");
|
||||
activePresenceGauge.set(serviceVersion, count, {
|
||||
presence_name,
|
||||
version,
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(Array.from(ips).map(async ([ip, { presences, sessions }]) => {
|
||||
const parsed = await lookupIp(ip);
|
||||
if (parsed) {
|
||||
activeIpsGauge.set(ip, sessions, {
|
||||
country: parsed.country,
|
||||
ip,
|
||||
latitude: parsed.latitude,
|
||||
longitude: parsed.longitude,
|
||||
presence_names: presences,
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -3,14 +3,18 @@ import { CronJob } from "cron";
|
||||
import debug from "debug";
|
||||
import { clearOldSessions } from "./functions/clearOldSessions.js";
|
||||
import createRedis from "./functions/createRedis.js";
|
||||
import { setCounter } from "./functions/setCounter.js";
|
||||
import { setSessionCounter } from "./functions/setSessionCounter.js";
|
||||
import "./tracing.js";
|
||||
import { updateActivePresenceGauge } from "./functions/updateActivePresenceGauge.js";
|
||||
import { reloadIpLocationApi } from "./functions/lookupIp.js";
|
||||
|
||||
export const redis = createRedis();
|
||||
|
||||
export const mainLog = debug("api-master");
|
||||
|
||||
debug("Starting cron job to clear old sessions");
|
||||
reloadIpLocationApi();
|
||||
|
||||
debug("Starting cron jobs");
|
||||
|
||||
void new CronJob(
|
||||
// Every 5 seconds
|
||||
@@ -26,7 +30,27 @@ void new CronJob(
|
||||
// Every second
|
||||
"* * * * * *",
|
||||
() => {
|
||||
setCounter();
|
||||
setSessionCounter();
|
||||
},
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
|
||||
void new CronJob(
|
||||
// Every 5 seconds
|
||||
"*/5 * * * * *",
|
||||
() => {
|
||||
updateActivePresenceGauge();
|
||||
},
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
|
||||
void new CronJob(
|
||||
// Every day at 9am
|
||||
"0 9 * * *",
|
||||
() => {
|
||||
reloadIpLocationApi();
|
||||
},
|
||||
undefined,
|
||||
true,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ValueType } from "@opentelemetry/api";
|
||||
import { PrometheusExporter } from "@opentelemetry/exporter-prometheus";
|
||||
import { MeterProvider } from "@opentelemetry/sdk-metrics";
|
||||
import { ClearableGaugeMetric, updatePrometheusMetrics } from "./functions/clearableGaugeMetric.js";
|
||||
|
||||
const prometheusExporter = new PrometheusExporter();
|
||||
|
||||
@@ -10,9 +11,21 @@ const provider = new MeterProvider({
|
||||
|
||||
const meter = provider.getMeter("nice");
|
||||
|
||||
export const counter = meter.createUpDownCounter("active_activites", {
|
||||
description: "Number of active activities",
|
||||
export const activeSessionsCounter = meter.createUpDownCounter("active_sessions", {
|
||||
description: "Number of active sessions",
|
||||
valueType: ValueType.INT,
|
||||
});
|
||||
|
||||
export const activePresenceGauge = new ClearableGaugeMetric(
|
||||
"active_presences",
|
||||
"Per presence name+version, active number of users",
|
||||
);
|
||||
|
||||
export const activeIpsGauge = new ClearableGaugeMetric(
|
||||
"active_ips",
|
||||
"Per ip, list of presences and the number of sessions",
|
||||
);
|
||||
|
||||
updatePrometheusMetrics(prometheusExporter);
|
||||
|
||||
prometheusExporter.startServer();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"rootDir": "src",
|
||||
"types": ["./environment.d.ts"],
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@premid/api-worker",
|
||||
"type": "module",
|
||||
"version": "0.0.9",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"description": "PreMiD's api",
|
||||
"license": "MPL-2.0",
|
||||
|
||||
10
apps/api-worker/src/constants.ts
Normal file
10
apps/api-worker/src/constants.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import process from "node:process";
|
||||
import { defu } from "defu";
|
||||
|
||||
const disabledFlags = process.env.DISABLED_FEATURE_FLAGS?.split(",") ?? [];
|
||||
const flags = Object.fromEntries(disabledFlags.map(flag => [flag, false]));
|
||||
|
||||
export const featureFlags = defu(flags, {
|
||||
WebSocketManager: true,
|
||||
SessionKeepAlive: true,
|
||||
});
|
||||
@@ -1,13 +1,10 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { resolve } from "node:path";
|
||||
import process from "node:process";
|
||||
import { useSentry } from "@envelop/sentry";
|
||||
import { maxAliasesPlugin } from "@escape.tech/graphql-armor-max-aliases";
|
||||
import { maxDepthPlugin } from "@escape.tech/graphql-armor-max-depth";
|
||||
import { maxDirectivesPlugin } from "@escape.tech/graphql-armor-max-directives";
|
||||
import { maxTokensPlugin } from "@escape.tech/graphql-armor-max-tokens";
|
||||
import fastifyWebsocket from "@fastify/websocket";
|
||||
import { defu } from "defu";
|
||||
import fastify from "fastify";
|
||||
|
||||
import { createSchema, createYoga } from "graphql-yoga";
|
||||
@@ -15,6 +12,7 @@ import type { FastifyReply, FastifyRequest } from "fastify";
|
||||
import { Socket } from "../classes/Socket.js";
|
||||
import { resolvers } from "../graphql/resolvers/v5/index.js";
|
||||
import { sessionKeepAlive } from "../routes/sessionKeepAlive.js";
|
||||
import { featureFlags } from "../constants.js";
|
||||
import createRedis from "./createRedis.js";
|
||||
|
||||
export interface FastifyContext {
|
||||
@@ -48,7 +46,7 @@ export default async function createServer() {
|
||||
maxDepthPlugin(),
|
||||
maxDirectivesPlugin(),
|
||||
maxTokensPlugin(),
|
||||
useSentry(),
|
||||
/* useSentry(), */
|
||||
],
|
||||
schema: createSchema<FastifyContext>({
|
||||
resolvers,
|
||||
@@ -87,15 +85,7 @@ export default async function createServer() {
|
||||
});
|
||||
|
||||
app.get("/v5/feature-flags", async (request, reply) => {
|
||||
const disabledFlags = process.env.DISABLED_FEATURE_FLAGS?.split(",") ?? [];
|
||||
const flags = Object.fromEntries(disabledFlags.map(flag => [flag, false]));
|
||||
|
||||
const test = defu(flags, {
|
||||
WebSocketManager: true,
|
||||
SessionKeepAlive: true,
|
||||
});
|
||||
|
||||
void reply.send(test);
|
||||
void reply.send(featureFlags);
|
||||
});
|
||||
|
||||
app.post("/v5/session-keep-alive", sessionKeepAlive);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type } from "arktype";
|
||||
import { GraphQLError } from "graphql";
|
||||
import { redis } from "../../../../functions/createServer.js";
|
||||
import type { MutationResolvers } from "../../../../generated/graphql-v5.js";
|
||||
|
||||
@@ -15,7 +16,7 @@ const mutation: MutationResolvers["addScience"] = async (_parent, input) => {
|
||||
const out = addScienceSchema(input);
|
||||
|
||||
if (out instanceof type.errors)
|
||||
throw new Error(out.summary);
|
||||
throw new GraphQLError(out.summary);
|
||||
|
||||
await redis.hset(
|
||||
"pmd-api.scienceUpdates",
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { type } from "arktype";
|
||||
import { GraphQLError } from "graphql";
|
||||
import type { MutationResolvers } from "../../../../generated/graphql-v5.js";
|
||||
import { redis } from "../../../../functions/createServer.js";
|
||||
|
||||
const heartbeatSchema = type({
|
||||
identifier: "string.uuid & string.lower",
|
||||
presences: {
|
||||
"identifier": "string.uuid & string.lower",
|
||||
"presence?": {
|
||||
service: "string.trim",
|
||||
version: "string.semver",
|
||||
language: "string.trim",
|
||||
since: "number.epoch",
|
||||
},
|
||||
extension: {
|
||||
"extension": {
|
||||
"version": "string.semver",
|
||||
"language": "string.trim",
|
||||
"connected?": {
|
||||
@@ -19,19 +21,29 @@ const heartbeatSchema = type({
|
||||
},
|
||||
});
|
||||
|
||||
const mutation: MutationResolvers["heartbeat"] = async (_parent, input) => {
|
||||
const mutation: MutationResolvers["heartbeat"] = async (_parent, input, context) => {
|
||||
const out = heartbeatSchema(input);
|
||||
|
||||
if (out instanceof type.errors)
|
||||
throw new Error(out.summary);
|
||||
throw new GraphQLError(out.summary);
|
||||
|
||||
// ! Disabled for now
|
||||
/* await redis.setex(
|
||||
`pmd-api.heartbeatUpdates.${data.identifier}`,
|
||||
// 5 minutes
|
||||
300,
|
||||
JSON.stringify(data)
|
||||
); */
|
||||
//* Get the user's IP address from Cloudflare headers or fallback to the request IP
|
||||
const userIp = context.request.headers.get("cf-connecting-ip") || context.request.ip;
|
||||
|
||||
// * Use Redis Hash with 'service' in the key to store heartbeat data
|
||||
const redisKey = `pmd-api.heartbeatUpdates.${out.identifier}`;
|
||||
await redis.hset(redisKey, {
|
||||
service: out.presence?.service,
|
||||
version: out.presence?.version,
|
||||
language: out.presence?.language,
|
||||
since: out.presence?.since.toString(),
|
||||
extension_version: out.extension.version,
|
||||
extension_language: out.extension.language,
|
||||
extension_connected_app: out.extension.connected?.app?.toString(),
|
||||
extension_connected_discord: out.extension.connected?.discord?.toString(),
|
||||
ip_address: userIp,
|
||||
});
|
||||
await redis.expire(redisKey, 300);
|
||||
|
||||
return {
|
||||
__typename: "HeartbeatResult",
|
||||
|
||||
@@ -3,8 +3,6 @@ import process from "node:process";
|
||||
import * as Sentry from "@sentry/node";
|
||||
import { connect } from "mongoose";
|
||||
import "./tracing.js";
|
||||
|
||||
// eslint-disable-next-line perfectionist/sort-imports
|
||||
import createServer from "./functions/createServer.js";
|
||||
|
||||
// TODO SETUP SENTRY
|
||||
|
||||
@@ -4,6 +4,7 @@ import { type } from "arktype";
|
||||
import { Routes } from "discord-api-types/v10";
|
||||
import type { FastifyReply, FastifyRequest } from "fastify";
|
||||
import { redis } from "../functions/createServer.js";
|
||||
import { featureFlags } from "../constants.js";
|
||||
|
||||
const schema = type({
|
||||
token: "string.trim",
|
||||
@@ -13,6 +14,9 @@ const schema = type({
|
||||
});
|
||||
|
||||
export async function sessionKeepAlive(request: FastifyRequest, reply: FastifyReply) {
|
||||
if (!featureFlags.SessionKeepAlive)
|
||||
return reply.status(202).send();
|
||||
|
||||
//* Get the headers
|
||||
const out = schema({
|
||||
token: request.headers["x-token"],
|
||||
@@ -27,17 +31,15 @@ export async function sessionKeepAlive(request: FastifyRequest, reply: FastifyRe
|
||||
if (!await isTokenValid(out.token))
|
||||
return reply.status(400).send({ code: "INVALID_TOKEN", message: "The token is invalid" });
|
||||
|
||||
await redis.hset(
|
||||
"pmd-api.sessions",
|
||||
out.scienceId,
|
||||
JSON.stringify({
|
||||
session: out.session,
|
||||
token: out.token,
|
||||
lastUpdated: Date.now(),
|
||||
}),
|
||||
);
|
||||
const redisKey = `pmd-api.sessions.${out.scienceId}`;
|
||||
await redis.hset(redisKey, {
|
||||
session: out.session,
|
||||
token: out.token,
|
||||
lastUpdated: Date.now(),
|
||||
});
|
||||
await redis.expire(redisKey, 300); // 5 minutes
|
||||
|
||||
const interval = Number.parseInt(process.env.SESSION_KEEP_ALIVE_INTERVAL ?? "5000");
|
||||
const interval = Number.parseInt(process.env.SESSION_KEEP_ALIVE_INTERVAL ?? "5000"); // 5 seconds
|
||||
|
||||
return reply.status(200).send({
|
||||
code: "OK",
|
||||
|
||||
@@ -38,5 +38,10 @@
|
||||
"prettier": "^3.2.5",
|
||||
"typescript": "^5.5.4",
|
||||
"vitest": "^2.0.2"
|
||||
},
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"ip-location-api@1.0.0": "patches/ip-location-api@1.0.0.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
62
patches/ip-location-api@1.0.0.patch
Normal file
62
patches/ip-location-api@1.0.0.patch
Normal file
@@ -0,0 +1,62 @@
|
||||
diff --git a/browser/country/README.md b/browser/country/README.md
|
||||
deleted file mode 100644
|
||||
index ac8fc934b4998f2a2cb7a92bf68bbdadd9e3d36d..0000000000000000000000000000000000000000
|
||||
diff --git a/browser/country-extra/README.md b/browser/country-extra/README.md
|
||||
deleted file mode 100644
|
||||
index 71e7237722915b2697b56ccb14171524eb4b40fb..0000000000000000000000000000000000000000
|
||||
diff --git a/browser/geocode/README.md b/browser/geocode/README.md
|
||||
deleted file mode 100644
|
||||
index 9d9a2205061f332b363b82d7561a0e3829d5bf2c..0000000000000000000000000000000000000000
|
||||
diff --git a/browser/geocode-extra/README.md b/browser/geocode-extra/README.md
|
||||
deleted file mode 100644
|
||||
index 38e17eebdd8532d07b460fbe7f385f36625ece9d..0000000000000000000000000000000000000000
|
||||
diff --git a/src/db.mjs b/src/db.mjs
|
||||
index 378b8a22084f860cc89720d1783a235c034717b2..cbffe1eaa84a94df536059d4b4af3f8f5ceb0ca7 100644
|
||||
--- a/src/db.mjs
|
||||
+++ b/src/db.mjs
|
||||
@@ -33,7 +33,12 @@ export const update = async () => {
|
||||
if(refreshTmpDir || !fsSync.existsSync(setting.tmpDataDir)){
|
||||
// refresh tmp folder
|
||||
await rimraf(setting.tmpDataDir)
|
||||
- await fs.mkdir(setting.tmpDataDir)
|
||||
+ await fs.mkdir(setting.tmpDataDir, {recursive: true})
|
||||
+ }
|
||||
+
|
||||
+ // When specifying a custom dataDir, it doesn't always exists
|
||||
+ if (!fsSync.existsSync(setting.dataDir)){
|
||||
+ await fs.mkdir(setting.dataDir, {recursive: true})
|
||||
}
|
||||
|
||||
console.log('Downloading database')
|
||||
diff --git a/src/main.mjs b/src/main.mjs
|
||||
index d001aca60902bc7fe41271c6fa7a0b6648607b15..5b2c125d8e7590afee82c794bf771accd656b2b7 100644
|
||||
--- a/src/main.mjs
|
||||
+++ b/src/main.mjs
|
||||
@@ -3,7 +3,7 @@ import fs from 'fs/promises'
|
||||
import fsSync from 'fs'
|
||||
import path from 'path'
|
||||
import { exec } from 'child_process'
|
||||
-
|
||||
+import { fileURLToPath } from "url"
|
||||
import { countries, continents } from 'countries-list'
|
||||
|
||||
import { setting, setSetting, getSettingCmd } from './setting.mjs'
|
||||
@@ -14,6 +14,9 @@ const v6db = setting.v6
|
||||
const locFieldHash = setting.locFieldHash
|
||||
const mainFieldHash = setting.mainFieldHash
|
||||
|
||||
+const __filename = fileURLToPath(import.meta.url)
|
||||
+const __dirname = path.dirname(__filename)
|
||||
+
|
||||
//---------------------------------------
|
||||
// Database lookup
|
||||
//---------------------------------------
|
||||
@@ -235,7 +238,7 @@ export const updateDb = (_setting) => {
|
||||
// However, db.js import many external modules, it makes slow down the startup time and uses more memory.
|
||||
// Therefore, we use exec() to run the script in the other process.
|
||||
return new Promise((resolve, reject) => {
|
||||
- var cmd = 'node ' + path.resolve(__dirname, '..', 'script', 'updatedb.js')
|
||||
+ var cmd = 'node ' + path.resolve(__dirname, '..', 'script', 'updatedb.mjs')
|
||||
var arg
|
||||
if(_setting){
|
||||
var oldSetting = Object.assign({}, setting)
|
||||
204
pnpm-lock.yaml
generated
204
pnpm-lock.yaml
generated
@@ -4,6 +4,11 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
patchedDependencies:
|
||||
ip-location-api@1.0.0:
|
||||
hash: rjznwunaa6otlvuetmywn36scu
|
||||
path: patches/ip-location-api@1.0.0.patch
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -53,9 +58,6 @@ importers:
|
||||
|
||||
apps/api-master:
|
||||
dependencies:
|
||||
'@discordjs/rest':
|
||||
specifier: ^2.3.0
|
||||
version: 2.4.0
|
||||
'@envelop/sentry':
|
||||
specifier: ^9.0.0
|
||||
version: 9.0.0(@envelop/core@5.0.2)(@sentry/node@8.30.0)(graphql@16.9.0)
|
||||
@@ -80,6 +82,12 @@ importers:
|
||||
ioredis:
|
||||
specifier: ^5.3.2
|
||||
version: 5.4.1
|
||||
ip-location-api:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0(patch_hash=rjznwunaa6otlvuetmywn36scu)(debug@4.3.7)
|
||||
ky:
|
||||
specifier: ^1.7.2
|
||||
version: 1.7.2
|
||||
p-limit:
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0
|
||||
@@ -177,7 +185,7 @@ importers:
|
||||
devDependencies:
|
||||
vitepress:
|
||||
specifier: 1.3.1
|
||||
version: 1.3.1(@algolia/client-search@5.4.0)(@types/node@22.5.4)(change-case@4.1.2)(postcss@8.4.45)(sass@1.78.0)(search-insights@2.17.2)(terser@5.32.0)(typescript@5.6.2)
|
||||
version: 1.3.1(@algolia/client-search@5.4.0)(@types/node@22.5.4)(axios@1.7.7)(change-case@4.1.2)(postcss@8.4.45)(sass@1.78.0)(search-insights@2.17.2)(terser@5.32.0)(typescript@5.6.2)
|
||||
|
||||
apps/pd:
|
||||
dependencies:
|
||||
@@ -268,7 +276,7 @@ importers:
|
||||
version: 3.13.1(magicast@0.3.5)(rollup@4.21.2)(webpack-sources@3.2.3)
|
||||
'@nuxt/scripts':
|
||||
specifier: ^0.8.5
|
||||
version: 0.8.5(@nuxt/devtools@1.4.2(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3))(@unocss/webpack@0.62.3(rollup@4.21.2)(webpack@5.94.0(esbuild@0.23.1)))(@vue/compiler-core@3.5.4)(fuse.js@7.0.0)(ioredis@5.4.1)(magicast@0.3.5)(nuxt@3.13.1(@parcel/watcher@2.4.1)(@types/node@22.5.4)(eslint@9.9.1(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(sass@1.78.0)(terser@5.32.0)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue-tsc@2.1.6(typescript@5.5.4))(webpack-sources@3.2.3))(postcss@8.4.45)(rollup@4.21.2)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3)(webpack@5.94.0(esbuild@0.23.1))
|
||||
version: 0.8.5(@nuxt/devtools@1.4.2(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3))(@unocss/webpack@0.62.3(rollup@4.21.2)(webpack@5.94.0(esbuild@0.23.1)))(@vue/compiler-core@3.5.4)(axios@1.7.7)(fuse.js@7.0.0)(ioredis@5.4.1)(magicast@0.3.5)(nuxt@3.13.1(@parcel/watcher@2.4.1)(@types/node@22.5.4)(eslint@9.9.1(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(sass@1.78.0)(terser@5.32.0)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue-tsc@2.1.6(typescript@5.5.4))(webpack-sources@3.2.3))(postcss@8.4.45)(rollup@4.21.2)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3)(webpack@5.94.0(esbuild@0.23.1))
|
||||
'@nuxtjs/device':
|
||||
specifier: ^3.2.2
|
||||
version: 3.2.2(@parcel/watcher@2.4.1)(@types/node@22.5.4)(@vitest/ui@2.0.5(vitest@2.0.5))(h3@1.12.0)(ioredis@5.4.1)(jiti@1.21.6)(magicast@0.3.5)(nitropack@2.9.7(magicast@0.3.5)(webpack-sources@3.2.3))(nuxi@3.13.1)(optionator@0.9.4)(playwright-core@1.47.0)(rollup@4.21.2)(sass@1.78.0)(terser@5.32.0)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue-router@4.4.4(vue@3.5.4(typescript@5.5.4)))(vue-tsc@2.1.6(typescript@5.5.4))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3)
|
||||
@@ -1694,6 +1702,12 @@ packages:
|
||||
resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@fast-csv/format@5.0.0':
|
||||
resolution: {integrity: sha512-IyMpHwYIOGa2f0BJi6Wk55UF0oBA5urdIydoEDYxPo88LFbeb3Yr4rgpu98OAO1glUWheSnNtUgS80LE+/dqmw==}
|
||||
|
||||
'@fast-csv/parse@5.0.0':
|
||||
resolution: {integrity: sha512-ecF8tCm3jVxeRjEB6VPzmA+1wGaJ5JgaUX2uesOXdXD6qQp0B3EdshOIed4yT1Xlj/F2f8v4zHSo0Oi31L697g==}
|
||||
|
||||
'@fastify/accept-negotiator@1.1.0':
|
||||
resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -2959,7 +2973,6 @@ packages:
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.21.2':
|
||||
resolution: {integrity: sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.18.1':
|
||||
@@ -4028,6 +4041,9 @@ packages:
|
||||
avvio@8.3.2:
|
||||
resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==}
|
||||
|
||||
axios@1.7.7:
|
||||
resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==}
|
||||
|
||||
b4a@1.6.6:
|
||||
resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==}
|
||||
|
||||
@@ -4123,6 +4139,9 @@ packages:
|
||||
resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==}
|
||||
engines: {node: '>=16.20.1'}
|
||||
|
||||
buffer-crc32@0.2.13:
|
||||
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
|
||||
|
||||
buffer-crc32@1.0.0:
|
||||
resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
@@ -4515,6 +4534,9 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
countries-list@3.1.1:
|
||||
resolution: {integrity: sha512-nPklKJ5qtmY5MdBKw1NiBAoyx5Sa7p2yPpljZyQ7gyCN1m+eMFs9I6CT37Mxt8zvR5L3VzD3DJBE4WQzX3WF4A==}
|
||||
|
||||
crawler-user-agents@1.0.143:
|
||||
resolution: {integrity: sha512-tVnQF0rrrzmiwHL/ASloGdW4wQ5Cjfd1ujiUWfeDjqXR6vTxrN4bIWnDd1tgUhDWx3z0msiktWjI51r3Dnw0CA==}
|
||||
|
||||
@@ -4643,6 +4665,9 @@ packages:
|
||||
dataloader@2.2.2:
|
||||
resolution: {integrity: sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==}
|
||||
|
||||
dayjs@1.11.13:
|
||||
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
|
||||
|
||||
db0@0.1.4:
|
||||
resolution: {integrity: sha512-Ft6eCwONYxlwLjBXSJxw0t0RYtA5gW9mq8JfBXn9TtC0nDPlqePAhpv9v4g9aONBi6JI1OXHTKKkUYGd+BOrCA==}
|
||||
peerDependencies:
|
||||
@@ -5240,6 +5265,10 @@ packages:
|
||||
fast-content-type-parse@1.1.0:
|
||||
resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==}
|
||||
|
||||
fast-csv@5.0.1:
|
||||
resolution: {integrity: sha512-Q43zC4NdQD5MAWOVQOF8KA+D6ddvTJjX2ib8zqysm74jZhtk6+dc8C75/OqRV6Y9CLc4kgvbC3PLG8YL4YZfgw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
fast-decode-uri-component@1.0.1:
|
||||
resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
|
||||
|
||||
@@ -5382,6 +5411,15 @@ packages:
|
||||
focus-trap@7.5.4:
|
||||
resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==}
|
||||
|
||||
follow-redirects@1.15.9:
|
||||
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
|
||||
fontaine@0.5.0:
|
||||
resolution: {integrity: sha512-vPDSWKhVAfTx4hRKT777+N6Szh2pAosAuzLpbppZ6O3UdD/1m6OlHjNcC3vIbgkRTIcLjzySLHXzPeLO2rE8cA==}
|
||||
|
||||
@@ -5834,6 +5872,14 @@ packages:
|
||||
resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==}
|
||||
engines: {node: '>=12.22.0'}
|
||||
|
||||
ip-address@9.0.5:
|
||||
resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
ip-location-api@1.0.0:
|
||||
resolution: {integrity: sha512-kx2597JI+SzijYEZ0X8zG3u77wVlcCwoJ9feHt11+COa39o4gzPJC6D62P0O6AAwrT0yUPI7JSo10e5tpFBzUA==}
|
||||
engines: {node: '>=14.8.0'}
|
||||
|
||||
ipaddr.js@1.9.1:
|
||||
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
||||
engines: {node: '>= 0.10'}
|
||||
@@ -6067,6 +6113,9 @@ packages:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
hasBin: true
|
||||
|
||||
jsbn@1.1.0:
|
||||
resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==}
|
||||
|
||||
jsdoc-type-pratt-parser@4.1.0:
|
||||
resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -6147,6 +6196,10 @@ packages:
|
||||
kolorist@1.8.0:
|
||||
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
|
||||
|
||||
ky@1.7.2:
|
||||
resolution: {integrity: sha512-OzIvbHKKDpi60TnF9t7UUVAF1B4mcqc02z5PIvrm08Wyb+yOcz63GRvEuVxNT18a9E1SrNouhB4W2NNLeD7Ykg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
launch-editor@2.9.1:
|
||||
resolution: {integrity: sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==}
|
||||
|
||||
@@ -6220,12 +6273,33 @@ packages:
|
||||
lodash.defaults@4.2.0:
|
||||
resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
|
||||
|
||||
lodash.escaperegexp@4.1.2:
|
||||
resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==}
|
||||
|
||||
lodash.groupby@4.6.0:
|
||||
resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==}
|
||||
|
||||
lodash.isarguments@3.1.0:
|
||||
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
|
||||
|
||||
lodash.isboolean@3.0.3:
|
||||
resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
|
||||
|
||||
lodash.isequal@4.5.0:
|
||||
resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
|
||||
|
||||
lodash.isfunction@3.0.9:
|
||||
resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==}
|
||||
|
||||
lodash.isnil@4.0.0:
|
||||
resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==}
|
||||
|
||||
lodash.isplainobject@4.0.6:
|
||||
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||
|
||||
lodash.isundefined@3.0.1:
|
||||
resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==}
|
||||
|
||||
lodash.kebabcase@4.1.1:
|
||||
resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
|
||||
|
||||
@@ -7011,6 +7085,9 @@ packages:
|
||||
resolution: {integrity: sha512-U94a+eXHzct7vAd19GH3UQ2dH4Satbng0MyYTMaQatL0pvYYL5CTPR25HBhKtecl+4bfu1/i3vC6k0hydO5Vcw==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
pend@1.2.0:
|
||||
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
|
||||
|
||||
perfect-debounce@1.0.0:
|
||||
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
|
||||
|
||||
@@ -7339,6 +7416,9 @@ packages:
|
||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
pump@3.0.0:
|
||||
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
|
||||
|
||||
@@ -7807,6 +7887,9 @@ packages:
|
||||
sponge-case@1.0.1:
|
||||
resolution: {integrity: sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==}
|
||||
|
||||
sprintf-js@1.1.3:
|
||||
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
|
||||
|
||||
stable-hash@0.0.4:
|
||||
resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==}
|
||||
|
||||
@@ -8884,6 +8967,10 @@ packages:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yauzl@3.1.3:
|
||||
resolution: {integrity: sha512-JCCdmlJJWv7L0q/KylOekyRaUrdEoUxWkWVcgorosTROCFWiS9p2NNPE9Yb91ak7b1N5SxAZEliWpspbZccivw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yocto-queue@0.1.0:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -10243,6 +10330,23 @@ snapshots:
|
||||
|
||||
'@eslint/object-schema@2.1.4': {}
|
||||
|
||||
'@fast-csv/format@5.0.0':
|
||||
dependencies:
|
||||
lodash.escaperegexp: 4.1.2
|
||||
lodash.isboolean: 3.0.3
|
||||
lodash.isequal: 4.5.0
|
||||
lodash.isfunction: 3.0.9
|
||||
lodash.isnil: 4.0.0
|
||||
|
||||
'@fast-csv/parse@5.0.0':
|
||||
dependencies:
|
||||
lodash.escaperegexp: 4.1.2
|
||||
lodash.groupby: 4.6.0
|
||||
lodash.isfunction: 3.0.9
|
||||
lodash.isnil: 4.0.0
|
||||
lodash.isundefined: 3.0.1
|
||||
lodash.uniq: 4.5.0
|
||||
|
||||
'@fastify/accept-negotiator@1.1.0':
|
||||
optional: true
|
||||
|
||||
@@ -11140,7 +11244,7 @@ snapshots:
|
||||
- supports-color
|
||||
- webpack-sources
|
||||
|
||||
'@nuxt/devtools-ui-kit@1.4.1(@nuxt/devtools@1.4.2(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3))(@unocss/webpack@0.62.3(rollup@4.21.2)(webpack@5.94.0(esbuild@0.23.1)))(@vue/compiler-core@3.5.4)(fuse.js@7.0.0)(magicast@0.3.5)(nuxt@3.13.1(@parcel/watcher@2.4.1)(@types/node@22.5.4)(eslint@9.9.1(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(sass@1.78.0)(terser@5.32.0)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue-tsc@2.1.6(typescript@5.5.4))(webpack-sources@3.2.3))(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3)(webpack@5.94.0(esbuild@0.23.1))':
|
||||
'@nuxt/devtools-ui-kit@1.4.1(@nuxt/devtools@1.4.2(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3))(@unocss/webpack@0.62.3(rollup@4.21.2)(webpack@5.94.0(esbuild@0.23.1)))(@vue/compiler-core@3.5.4)(axios@1.7.7)(fuse.js@7.0.0)(magicast@0.3.5)(nuxt@3.13.1(@parcel/watcher@2.4.1)(@types/node@22.5.4)(eslint@9.9.1(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(sass@1.78.0)(terser@5.32.0)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue-tsc@2.1.6(typescript@5.5.4))(webpack-sources@3.2.3))(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3)(webpack@5.94.0(esbuild@0.23.1))':
|
||||
dependencies:
|
||||
'@iconify-json/carbon': 1.2.1
|
||||
'@iconify-json/logos': 1.2.0
|
||||
@@ -11156,7 +11260,7 @@ snapshots:
|
||||
'@unocss/preset-mini': 0.62.3
|
||||
'@unocss/reset': 0.62.3
|
||||
'@vueuse/core': 11.0.3(vue@3.5.4(typescript@5.5.4))
|
||||
'@vueuse/integrations': 11.0.3(focus-trap@7.5.4)(fuse.js@7.0.0)(vue@3.5.4(typescript@5.5.4))
|
||||
'@vueuse/integrations': 11.0.3(axios@1.7.7)(focus-trap@7.5.4)(fuse.js@7.0.0)(vue@3.5.4(typescript@5.5.4))
|
||||
'@vueuse/nuxt': 11.0.3(magicast@0.3.5)(nuxt@3.13.1(@parcel/watcher@2.4.1)(@types/node@22.5.4)(eslint@9.9.1(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(sass@1.78.0)(terser@5.32.0)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue-tsc@2.1.6(typescript@5.5.4))(webpack-sources@3.2.3))(rollup@4.21.2)(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3)
|
||||
defu: 6.1.4
|
||||
focus-trap: 7.5.4
|
||||
@@ -11534,10 +11638,10 @@ snapshots:
|
||||
- supports-color
|
||||
- webpack-sources
|
||||
|
||||
'@nuxt/scripts@0.8.5(@nuxt/devtools@1.4.2(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3))(@unocss/webpack@0.62.3(rollup@4.21.2)(webpack@5.94.0(esbuild@0.23.1)))(@vue/compiler-core@3.5.4)(fuse.js@7.0.0)(ioredis@5.4.1)(magicast@0.3.5)(nuxt@3.13.1(@parcel/watcher@2.4.1)(@types/node@22.5.4)(eslint@9.9.1(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(sass@1.78.0)(terser@5.32.0)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue-tsc@2.1.6(typescript@5.5.4))(webpack-sources@3.2.3))(postcss@8.4.45)(rollup@4.21.2)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3)(webpack@5.94.0(esbuild@0.23.1))':
|
||||
'@nuxt/scripts@0.8.5(@nuxt/devtools@1.4.2(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3))(@unocss/webpack@0.62.3(rollup@4.21.2)(webpack@5.94.0(esbuild@0.23.1)))(@vue/compiler-core@3.5.4)(axios@1.7.7)(fuse.js@7.0.0)(ioredis@5.4.1)(magicast@0.3.5)(nuxt@3.13.1(@parcel/watcher@2.4.1)(@types/node@22.5.4)(eslint@9.9.1(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(sass@1.78.0)(terser@5.32.0)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue-tsc@2.1.6(typescript@5.5.4))(webpack-sources@3.2.3))(postcss@8.4.45)(rollup@4.21.2)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3)(webpack@5.94.0(esbuild@0.23.1))':
|
||||
dependencies:
|
||||
'@nuxt/devtools-kit': 1.4.1(magicast@0.3.5)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(webpack-sources@3.2.3)
|
||||
'@nuxt/devtools-ui-kit': 1.4.1(@nuxt/devtools@1.4.2(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3))(@unocss/webpack@0.62.3(rollup@4.21.2)(webpack@5.94.0(esbuild@0.23.1)))(@vue/compiler-core@3.5.4)(fuse.js@7.0.0)(magicast@0.3.5)(nuxt@3.13.1(@parcel/watcher@2.4.1)(@types/node@22.5.4)(eslint@9.9.1(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(sass@1.78.0)(terser@5.32.0)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue-tsc@2.1.6(typescript@5.5.4))(webpack-sources@3.2.3))(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3)(webpack@5.94.0(esbuild@0.23.1))
|
||||
'@nuxt/devtools-ui-kit': 1.4.1(@nuxt/devtools@1.4.2(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3))(@unocss/webpack@0.62.3(rollup@4.21.2)(webpack@5.94.0(esbuild@0.23.1)))(@vue/compiler-core@3.5.4)(axios@1.7.7)(fuse.js@7.0.0)(magicast@0.3.5)(nuxt@3.13.1(@parcel/watcher@2.4.1)(@types/node@22.5.4)(eslint@9.9.1(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(sass@1.78.0)(terser@5.32.0)(typescript@5.5.4)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue-tsc@2.1.6(typescript@5.5.4))(webpack-sources@3.2.3))(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(sass@1.78.0)(terser@5.32.0))(vue@3.5.4(typescript@5.5.4))(webpack-sources@3.2.3)(webpack@5.94.0(esbuild@0.23.1))
|
||||
'@nuxt/kit': 3.13.1(magicast@0.3.5)(rollup@4.21.2)(webpack-sources@3.2.3)
|
||||
'@types/google.maps': 3.58.0
|
||||
'@types/stripe-v3': 3.1.33
|
||||
@@ -13514,7 +13618,7 @@ snapshots:
|
||||
pathe: 1.1.2
|
||||
sirv: 2.0.4
|
||||
tinyrainbow: 1.2.0
|
||||
vitest: 2.0.5(@types/node@20.16.5)(@vitest/ui@2.0.5)(happy-dom@15.7.4)(sass@1.78.0)(terser@5.32.0)
|
||||
vitest: 2.0.5(@types/node@22.5.4)(@vitest/ui@2.0.5)(happy-dom@15.0.0)(sass@1.78.0)(terser@5.32.0)
|
||||
|
||||
'@vitest/utils@2.0.5':
|
||||
dependencies:
|
||||
@@ -13747,24 +13851,26 @@ snapshots:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@vueuse/integrations@10.11.1(change-case@4.1.2)(focus-trap@7.5.4)(vue@3.5.4(typescript@5.6.2))':
|
||||
'@vueuse/integrations@10.11.1(axios@1.7.7)(change-case@4.1.2)(focus-trap@7.5.4)(vue@3.5.4(typescript@5.6.2))':
|
||||
dependencies:
|
||||
'@vueuse/core': 10.11.1(vue@3.5.4(typescript@5.6.2))
|
||||
'@vueuse/shared': 10.11.1(vue@3.5.4(typescript@5.6.2))
|
||||
vue-demi: 0.14.10(vue@3.5.4(typescript@5.6.2))
|
||||
optionalDependencies:
|
||||
axios: 1.7.7(debug@4.3.7)
|
||||
change-case: 4.1.2
|
||||
focus-trap: 7.5.4
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@vueuse/integrations@11.0.3(focus-trap@7.5.4)(fuse.js@7.0.0)(vue@3.5.4(typescript@5.5.4))':
|
||||
'@vueuse/integrations@11.0.3(axios@1.7.7)(focus-trap@7.5.4)(fuse.js@7.0.0)(vue@3.5.4(typescript@5.5.4))':
|
||||
dependencies:
|
||||
'@vueuse/core': 11.0.3(vue@3.5.4(typescript@5.5.4))
|
||||
'@vueuse/shared': 11.0.3(vue@3.5.4(typescript@5.5.4))
|
||||
vue-demi: 0.14.10(vue@3.5.4(typescript@5.5.4))
|
||||
optionalDependencies:
|
||||
axios: 1.7.7(debug@4.3.7)
|
||||
focus-trap: 7.5.4
|
||||
fuse.js: 7.0.0
|
||||
transitivePeerDependencies:
|
||||
@@ -14125,6 +14231,14 @@ snapshots:
|
||||
'@fastify/error': 3.4.1
|
||||
fastq: 1.17.1
|
||||
|
||||
axios@1.7.7(debug@4.3.7):
|
||||
dependencies:
|
||||
follow-redirects: 1.15.9(debug@4.3.7)
|
||||
form-data: 4.0.0
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
b4a@1.6.6: {}
|
||||
|
||||
babel-plugin-syntax-trailing-function-commas@7.0.0-beta.0: {}
|
||||
@@ -14252,6 +14366,8 @@ snapshots:
|
||||
|
||||
bson@6.8.0: {}
|
||||
|
||||
buffer-crc32@0.2.13: {}
|
||||
|
||||
buffer-crc32@1.0.0: {}
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
@@ -14727,6 +14843,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.6.2
|
||||
|
||||
countries-list@3.1.1: {}
|
||||
|
||||
crawler-user-agents@1.0.143: {}
|
||||
|
||||
crc-32@1.2.2: {}
|
||||
@@ -14865,6 +14983,8 @@ snapshots:
|
||||
|
||||
dataloader@2.2.2: {}
|
||||
|
||||
dayjs@1.11.13: {}
|
||||
|
||||
db0@0.1.4: {}
|
||||
|
||||
de-indent@1.0.2: {}
|
||||
@@ -15703,6 +15823,11 @@ snapshots:
|
||||
|
||||
fast-content-type-parse@1.1.0: {}
|
||||
|
||||
fast-csv@5.0.1:
|
||||
dependencies:
|
||||
'@fast-csv/format': 5.0.0
|
||||
'@fast-csv/parse': 5.0.0
|
||||
|
||||
fast-decode-uri-component@1.0.1: {}
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
@@ -15873,6 +15998,10 @@ snapshots:
|
||||
dependencies:
|
||||
tabbable: 6.2.0
|
||||
|
||||
follow-redirects@1.15.9(debug@4.3.7):
|
||||
optionalDependencies:
|
||||
debug: 4.3.7
|
||||
|
||||
fontaine@0.5.0(webpack-sources@3.2.3):
|
||||
dependencies:
|
||||
'@capsizecss/metrics': 2.2.0
|
||||
@@ -16457,6 +16586,22 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ip-address@9.0.5:
|
||||
dependencies:
|
||||
jsbn: 1.1.0
|
||||
sprintf-js: 1.1.3
|
||||
|
||||
ip-location-api@1.0.0(patch_hash=rjznwunaa6otlvuetmywn36scu)(debug@4.3.7):
|
||||
dependencies:
|
||||
axios: 1.7.7(debug@4.3.7)
|
||||
countries-list: 3.1.1
|
||||
dayjs: 1.11.13
|
||||
fast-csv: 5.0.1
|
||||
ip-address: 9.0.5
|
||||
yauzl: 3.1.3
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
ipaddr.js@1.9.1: {}
|
||||
|
||||
ipaddr.js@2.2.0: {}
|
||||
@@ -16679,6 +16824,8 @@ snapshots:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
jsbn@1.1.0: {}
|
||||
|
||||
jsdoc-type-pratt-parser@4.1.0: {}
|
||||
|
||||
jsesc@0.5.0: {}
|
||||
@@ -16739,6 +16886,8 @@ snapshots:
|
||||
|
||||
kolorist@1.8.0: {}
|
||||
|
||||
ky@1.7.2: {}
|
||||
|
||||
launch-editor@2.9.1:
|
||||
dependencies:
|
||||
picocolors: 1.1.0
|
||||
@@ -16836,10 +16985,24 @@ snapshots:
|
||||
|
||||
lodash.defaults@4.2.0: {}
|
||||
|
||||
lodash.escaperegexp@4.1.2: {}
|
||||
|
||||
lodash.groupby@4.6.0: {}
|
||||
|
||||
lodash.isarguments@3.1.0: {}
|
||||
|
||||
lodash.isboolean@3.0.3: {}
|
||||
|
||||
lodash.isequal@4.5.0: {}
|
||||
|
||||
lodash.isfunction@3.0.9: {}
|
||||
|
||||
lodash.isnil@4.0.0: {}
|
||||
|
||||
lodash.isplainobject@4.0.6: {}
|
||||
|
||||
lodash.isundefined@3.0.1: {}
|
||||
|
||||
lodash.kebabcase@4.1.1: {}
|
||||
|
||||
lodash.memoize@4.1.2: {}
|
||||
@@ -18049,6 +18212,8 @@ snapshots:
|
||||
|
||||
peek-readable@5.2.0: {}
|
||||
|
||||
pend@1.2.0: {}
|
||||
|
||||
perfect-debounce@1.0.0: {}
|
||||
|
||||
pg-int8@1.0.1: {}
|
||||
@@ -18357,6 +18522,8 @@ snapshots:
|
||||
forwarded: 0.2.0
|
||||
ipaddr.js: 1.9.1
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
pump@3.0.0:
|
||||
dependencies:
|
||||
end-of-stream: 1.4.4
|
||||
@@ -18890,6 +19057,8 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.6.3
|
||||
|
||||
sprintf-js@1.1.3: {}
|
||||
|
||||
stable-hash@0.0.4: {}
|
||||
|
||||
stackback@0.0.2: {}
|
||||
@@ -19757,7 +19926,7 @@ snapshots:
|
||||
sass: 1.78.0
|
||||
terser: 5.32.0
|
||||
|
||||
vitepress@1.3.1(@algolia/client-search@5.4.0)(@types/node@22.5.4)(change-case@4.1.2)(postcss@8.4.45)(sass@1.78.0)(search-insights@2.17.2)(terser@5.32.0)(typescript@5.6.2):
|
||||
vitepress@1.3.1(@algolia/client-search@5.4.0)(@types/node@22.5.4)(axios@1.7.7)(change-case@4.1.2)(postcss@8.4.45)(sass@1.78.0)(search-insights@2.17.2)(terser@5.32.0)(typescript@5.6.2):
|
||||
dependencies:
|
||||
'@docsearch/css': 3.6.1
|
||||
'@docsearch/js': 3.6.1(@algolia/client-search@5.4.0)(search-insights@2.17.2)
|
||||
@@ -19768,7 +19937,7 @@ snapshots:
|
||||
'@vue/devtools-api': 7.4.5
|
||||
'@vue/shared': 3.5.4
|
||||
'@vueuse/core': 10.11.1(vue@3.5.4(typescript@5.6.2))
|
||||
'@vueuse/integrations': 10.11.1(change-case@4.1.2)(focus-trap@7.5.4)(vue@3.5.4(typescript@5.6.2))
|
||||
'@vueuse/integrations': 10.11.1(axios@1.7.7)(change-case@4.1.2)(focus-trap@7.5.4)(vue@3.5.4(typescript@5.6.2))
|
||||
focus-trap: 7.5.4
|
||||
mark.js: 8.11.1
|
||||
minisearch: 7.1.0
|
||||
@@ -20181,6 +20350,11 @@ snapshots:
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
|
||||
yauzl@3.1.3:
|
||||
dependencies:
|
||||
buffer-crc32: 0.2.13
|
||||
pend: 1.2.0
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
yocto-queue@1.1.1: {}
|
||||
|
||||
Reference in New Issue
Block a user