logging#19, max 5MB#18,addinsert/update#14,drizzle

This commit is contained in:
Your Name 2025-01-30 16:16:20 +01:00
parent 9f25253ade
commit 5707d1bb1f
22 changed files with 1073 additions and 100 deletions

13
.vscode/launch.json vendored
View File

@ -1,13 +1,14 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
"type": "node",
"request": "attach",
"name": "Attach to Nx Serve",
"port": 9229,
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/dist/api/**/*.js"],
"skipFiles": ["<node_internals>/**"]
}
]
}

View File

@ -2,10 +2,11 @@ import { Module } from '@nestjs/common';
import { DecksController } from './decks.controller';
import { DrizzleService } from './drizzle.service';
import { ProxyController } from './proxy.controller';
import { SqlLoggerService } from './sql-logger.service';
@Module({
imports: [],
controllers: [DecksController, ProxyController],
providers: [DrizzleService],
providers: [DrizzleService, SqlLoggerService],
})
export class AppModule {}

View File

@ -25,9 +25,8 @@ export class DecksController {
if (entry.bildname && entry.bildid) {
decks[deckname].images.push({
name: entry.bildname,
id: entry.bildid,
iconindex: entry.iconindex,
boxid: entry.id,
bildid: entry.bildid,
id: entry.id,
x1: entry.x1,
x2: entry.x2,
y1: entry.y1,
@ -72,9 +71,8 @@ export class DecksController {
if (entry.bildname && entry.bildid) {
deck.images.push({
name: entry.bildname,
id: entry.bildid,
iconindex: entry.iconindex,
boxid: entry.id,
bildid: entry.bildid,
id: entry.id,
x1: entry.x1,
x2: entry.x2,
y1: entry.y1,
@ -141,10 +139,10 @@ export class DecksController {
return this.drizzleService.moveImage(bildid, data.targetDeckId, user);
}
@Put('boxes/:boxId')
@Put('boxes/:id')
async updateBox(
@Request() req,
@Param('boxId') boxId: number,
@Param('id') id: number,
@Body()
data: {
due?: number;
@ -156,6 +154,6 @@ export class DecksController {
},
) {
const user: User = req['user'];
return this.drizzleService.updateBox(boxId, data, user);
return this.drizzleService.updateBox(id, data, user);
}
}

View File

@ -1,12 +1,23 @@
// drizzle.service.ts
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { and, eq, isNull, sql } from 'drizzle-orm';
import { and, eq, sql } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/libsql';
import { Deck, User } from '../db/schema';
import { Deck, SelectDeck, User } from '../db/schema';
import { SqlLoggerService } from './sql-logger.service';
@Injectable()
export class DrizzleService {
private db = drizzle('file:local.db');
// private readonly logger = new Logger(DrizzleService.name);
private db: any;
constructor(private sqlLogger: SqlLoggerService) {
this.db = drizzle('file:local.db', {
logger: {
logQuery: (query: string, params: any[]) => {
this.sqlLogger.logQuery(query, params);
},
},
});
}
/**
* Methode zum Abrufen aller Decks eines Benutzers
@ -80,48 +91,82 @@ export class DrizzleService {
/**
* Methode zum Aktualisieren eines Bildes innerhalb eines Decks
* Die Methode erhält jetzt auch den 'inserted' Timestamp
*/
async updateImage(
data: {
deckname: string;
bildname: string;
bildid: string;
boxes: Array<{ x1: number; x2: number; y1: number; y2: number }>;
inserted: string; // Neuer Parameter für den 'inserted' Timestamp
boxes: Array<{ x1: number; x2: number; y1: number; y2: number; id?: number }>;
},
user: User,
) {
const existingDeck = await this.getDeckByName(data.deckname, user);
if (existingDeck.length === 0) {
// Schritt 1: Überprüfen, ob das Deck existiert
const existingDecks: SelectDeck[] = await this.getDeckByName(data.deckname, user);
if (existingDecks.length === 0) {
throw new HttpException('Deck not found', HttpStatus.NOT_FOUND);
}
// Lösche vorhandene Einträge für das Bild
await this.db.delete(Deck).where(and(eq(Deck.deckname, data.deckname), eq(Deck.bildid, data.bildid), eq(Deck.user, user.email)));
// Schritt 2: Trennen der neuen und bestehenden Einträge
const newEntries = data.boxes.filter(b => !b.id);
const existingEntries = data.boxes.filter(b => b.id);
// Schritt 3: Einfügen neuer Einträge
const insertedImages: any = [];
for (let index = 0; index < data.boxes.length; index++) {
const box = data.boxes[index];
for (let index = 0; index < newEntries.length; index++) {
const box = newEntries[index];
const result = await this.db
.insert(Deck)
.values({
deckname: data.deckname,
bildname: data.bildname,
bildid: data.bildid,
iconindex: index,
x1: box.x1,
x2: box.x2,
y1: box.y1,
y2: box.y2,
user: user.email,
inserted: data.inserted, // Setze 'inserted' auf den übergebenen Wert
// 'updated' wird automatisch von der DB gesetzt
// 'inserted' und 'updated' werden automatisch von der DB gesetzt
})
.returning();
insertedImages.push(result);
}
// Schritt 4: Aktualisieren bestehender Einträge
for (let index = 0; index < existingEntries.length; index++) {
const box = existingEntries[index];
const existingDeck = existingDecks.find(d => d.id === box.id);
if (existingDeck && (existingDeck.x1 !== box.x1 || existingDeck.x2 !== box.x2 || existingDeck.y1 !== box.y1 || existingDeck.y2 !== box.y2)) {
const result = await this.db
.update(Deck)
.set({
x1: box.x1,
x2: box.x2,
y1: box.y1,
y2: box.y2,
updated: sql`CURRENT_TIMESTAMP`, // Setze 'updated' auf CURRENT_TIMESTAMP
})
.where(and(eq(Deck.user, user.email), eq(Deck.bildid, data.bildid), eq(Deck.deckname, data.deckname), eq(Deck.id, box.id!!)));
if (result.rowsAffected === 0) {
throw new HttpException(`Box with id ${box.id} not found`, HttpStatus.NOT_FOUND);
}
}
}
// Schritt 5: Löschen von nicht mehr vorhandenen Einträgen
// Sammle alle bestehenden IDs aus der Datenbank für das gegebene deckname und bildid
const existingIdsInDb = existingDecks.filter(entry => entry.bildid === data.bildid && entry.deckname === data.deckname).map(entry => entry.id);
// Sammle alle eingehenden IDs aus den vorhandenen Einträgen
const incomingIds = existingEntries.map(entry => entry.id);
// Bestimme die IDs, die in der Datenbank existieren, aber nicht mehr in den eingehenden Daten vorhanden sind
const idsToDelete = existingIdsInDb.filter(id => !incomingIds.includes(id));
if (idsToDelete.length > 0) {
await this.db.delete(Deck).where(and(eq(Deck.deckname, data.deckname), eq(Deck.bildid, data.bildid), eq(Deck.user, user.email), sql`id in ${idsToDelete}`));
}
return { status: 'success', inserted_images: insertedImages };
}
@ -141,30 +186,6 @@ export class DrizzleService {
await this.db.delete(Deck).where(and(eq(Deck.bildid, bildid), eq(Deck.user, user.email)));
for (const deck of affectedDecks) {
const remainingImages = await this.db
.select()
.from(Deck)
.where(and(eq(Deck.deckname, deck.deckname), eq(Deck.bildid, bildid), eq(Deck.user, user.email)))
.all();
if (remainingImages.length === 0) {
const emptyDeckEntry = await this.db
.select()
.from(Deck)
.where(and(eq(Deck.deckname, deck.deckname), isNull(Deck.bildid), eq(Deck.user, user.email)))
.all();
if (emptyDeckEntry.length === 0) {
await this.db.insert(Deck).values({
deckname: deck.deckname,
user: user.email,
// 'inserted' und 'updated' werden automatisch von der DB gesetzt
});
}
}
}
return {
status: 'success',
message: `All entries for image ID "${bildid}" have been deleted.`,
@ -200,7 +221,7 @@ export class DrizzleService {
* Methode zum Aktualisieren einer Box
*/
async updateBox(
boxId: number,
id: number,
data: {
due?: number;
ivl?: number;
@ -220,7 +241,7 @@ export class DrizzleService {
const result = await this.db
.update(Deck)
.set(updateData)
.where(and(eq(Deck.id, boxId), eq(Deck.user, user.email)));
.where(and(eq(Deck.id, id), eq(Deck.user, user.email)));
if (result.rowsAffected === 0) {
throw new HttpException('Box not found', HttpStatus.NOT_FOUND);

View File

@ -0,0 +1,36 @@
// sql-logger.service.ts
import { Injectable, Logger } from '@nestjs/common';
import type { ChalkInstance } from 'chalk';
import chalk from 'chalk';
@Injectable()
export class SqlLoggerService {
private readonly logger = new Logger(SqlLoggerService.name);
private chalkInstance: ChalkInstance;
constructor() {
this.chalkInstance = chalk;
}
logQuery(query: string, params: any[], sourceIp?: string) {
const timestamp = new Date().toISOString().replace('T', ' ').replace('Z', '');
let logMessage = `[DrizzleORM] Info\t${timestamp}`;
// Optional source IP
if (sourceIp) {
logMessage += ` IP: ${sourceIp}`;
}
// Colored query and params using chalk directly
const coloredQuery = chalk.blueBright(`Query: ${query}`);
const coloredParams = chalk.yellow(`Params: ${JSON.stringify(params)}`);
logMessage += ` - ${coloredQuery} - ${coloredParams}`;
// Add timing indicator
logMessage += chalk.gray(' +2ms');
console.log(logMessage);
}
}

View File

@ -9,7 +9,6 @@ export const Deck = table(
deckname: text('deckname').notNull(),
bildname: text('bildname'),
bildid: text('bildid'),
iconindex: integer('iconindex'),
x1: real('x1'),
x2: real('x2'),
y1: real('y1'),

View File

@ -5,20 +5,20 @@
import { Logger } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app/app.module';
import { json, urlencoded } from 'express';
import { AppModule } from './app/app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const app = await NestFactory.create(AppModule, {
logger: ['log', 'error', 'warn', 'debug', 'verbose'], // Aktiviere alle Log-Level
});
app.use(json({ limit: '50mb' }));
app.use(urlencoded({ limit: '50mb', extended: true }));
const globalPrefix = 'api';
app.setGlobalPrefix(globalPrefix);
const port = process.env['PORT'] || 3000;
await app.listen(port);
Logger.log(
`🚀 Application is running on: http://localhost:${port}/${globalPrefix}`
);
Logger.log(`🚀 Application is running on: http://localhost:${port}/${globalPrefix}`);
}
bootstrap();

View File

@ -0,0 +1 @@
ALTER TABLE `Deck` DROP COLUMN `iconindex`;

View File

@ -0,0 +1,157 @@
{
"version": "6",
"dialect": "sqlite",
"id": "a0b561cf-8404-43ea-bc72-8b32671580bd",
"prevId": "1e5421d8-df58-434e-93c8-85dcbe6ec9ee",
"tables": {
"Deck": {
"name": "Deck",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"deckname": {
"name": "deckname",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"bildname": {
"name": "bildname",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"bildid": {
"name": "bildid",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"x1": {
"name": "x1",
"type": "real",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"x2": {
"name": "x2",
"type": "real",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"y1": {
"name": "y1",
"type": "real",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"y2": {
"name": "y2",
"type": "real",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"due": {
"name": "due",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"ivl": {
"name": "ivl",
"type": "real",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"factor": {
"name": "factor",
"type": "real",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"reps": {
"name": "reps",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"lapses": {
"name": "lapses",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"isGraduated": {
"name": "isGraduated",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"user": {
"name": "user",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"inserted": {
"name": "inserted",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "(CURRENT_TIMESTAMP)"
},
"updated": {
"name": "updated",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "(CURRENT_TIMESTAMP)"
}
},
"indexes": {
"email_idx": {
"name": "email_idx",
"columns": [
"id"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -22,6 +22,13 @@
"when": 1738103858394,
"tag": "0002_aromatic_zodiak",
"breakpoints": true
},
{
"idx": 3,
"version": "6",
"when": 1738235979220,
"tag": "0003_hard_cable",
"breakpoints": true
}
]
}

730
package-lock.json generated
View File

@ -54,6 +54,7 @@
"@swc/helpers": "~0.5.11",
"@types/fabric": "^5.3.9",
"@types/node": "~18.16.9",
"chalk": "^5.4.1",
"concurrently": "^9.1.2",
"drizzle-kit": "^0.30.2",
"drizzle-orm": "^0.38.4",
@ -5205,6 +5206,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@jest/console/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/@jest/console/node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@ -5347,6 +5365,36 @@
"concat-map": "0.0.1"
}
},
"node_modules/@jest/reporters/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/@jest/reporters/node_modules/chalk/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/reporters/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@ -5521,6 +5569,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@jest/transform/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/@jest/transform/node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@ -5556,6 +5621,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@jest/types/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
@ -7393,6 +7475,22 @@
"npm": ">=5.0.0"
}
},
"node_modules/@nuxtjs/opencollective/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/@nx/angular": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/angular/-/angular-20.3.2.tgz",
@ -7626,6 +7724,23 @@
}
}
},
"node_modules/@nx/js/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/@nx/js/node_modules/cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@ -8116,6 +8231,108 @@
"tslib": "^2.3.0"
}
},
"node_modules/@nx/nx-darwin-arm64": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.3.2.tgz",
"integrity": "sha512-lQOXMIPmE9o36TuZ+SX6iq7PPWa3s1fjNRqCujlviExX69245NNCMxd754gXlLrsxC1onrx/zmJciKmmEWDIiw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nx/nx-darwin-x64": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.3.2.tgz",
"integrity": "sha512-RvvSz4QYVOYOfC8sUE63b6dy8iHk2AEI0r1FF5FCQuqE1DdTeTjPETY2sY35tRqF+mO/6oLGp2+m9ti/ysRoTg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nx/nx-freebsd-x64": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.3.2.tgz",
"integrity": "sha512-KBDTyGn1evlZ17pupwRUDh2wrCMuHhP2j8cOCdgF5cl7vRki8BOK9yyL6jD11d/d/6DgXzy1jmQEX4Xx+AGCug==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nx/nx-linux-arm-gnueabihf": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.3.2.tgz",
"integrity": "sha512-mW+OcOnJEMvs7zD3aSwEG3z5M9bI4CuUU5Q/ePmnNzWIucRHpoAMNt/Sd+yu6L4+QttvoUf967uwcMsX8l4nrw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nx/nx-linux-arm64-gnu": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.3.2.tgz",
"integrity": "sha512-hbXpZqUvGY5aeEWvh0SNsiYjP1ytSM30XOT6qN6faLO2CL/7j9D2UB69SKOqF3TJOvuNU6cweFgZCxyGfXBYIQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nx/nx-linux-arm64-musl": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.3.2.tgz",
"integrity": "sha512-HXthtN7adXCNVWs2F4wIqq2f7BcKTjsEnqg2LWV5lm4hRYvMfEvPftb0tECsEhcSQQYcvIJnLfv3vtu9HZSfVA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nx/nx-linux-x64-gnu": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.3.2.tgz",
@ -8133,6 +8350,57 @@
"node": ">= 10"
}
},
"node_modules/@nx/nx-linux-x64-musl": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.3.2.tgz",
"integrity": "sha512-NrZ8L9of2GmYEM8GMJX6QRrLJlAwM+ds2rhdY1bxwpiyCNcD3IO/gzJlBs+kG4ly05F1u/X4k/FI5dXPpjUSgw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nx/nx-win32-arm64-msvc": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.3.2.tgz",
"integrity": "sha512-yLjacZND7C1XmsC0jfRLSgeLWZUw2Oz+u3nXNvj5JX6YHtYTVLFnRbTAcI+pG2Y6v0Otf2GKb3VT5d1mQb8JvA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nx/nx-win32-x64-msvc": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.3.2.tgz",
"integrity": "sha512-oDhcctfk0UB1V+Otp1161VKNMobzkFQxGyiEIjp0CjCBa2eRHC1r35L695F1Hj0bvLQPSni9XIe9evh2taeAkg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nx/web": {
"version": "20.3.2",
"resolved": "https://registry.npmjs.org/@nx/web/-/web-20.3.2.tgz",
@ -8522,6 +8790,23 @@
"yargs-parser": "21.1.1"
}
},
"node_modules/@nx/workspace/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/@opentelemetry/api": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
@ -11130,6 +11415,23 @@
"@babel/core": "^7.8.0"
}
},
"node_modules/babel-jest/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/babel-jest/node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@ -11961,16 +12263,13 @@
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
@ -12448,6 +12747,36 @@
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/concurrently/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/concurrently/node_modules/chalk/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/concurrently/node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
@ -14985,6 +15314,24 @@
"concat-map": "0.0.1"
}
},
"node_modules/eslint/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/eslint/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@ -16033,6 +16380,23 @@
"concat-map": "0.0.1"
}
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@ -17225,6 +17589,22 @@
"node": ">=12"
}
},
"node_modules/http-server/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
@ -18021,6 +18401,23 @@
"concat-map": "0.0.1"
}
},
"node_modules/jake/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jake/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -18084,6 +18481,23 @@
"npm": ">=6"
}
},
"node_modules/jest-circus/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-circus/node_modules/cosmiconfig": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
@ -18238,6 +18652,23 @@
"concat-map": "0.0.1"
}
},
"node_modules/jest-config/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-config/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@ -18299,6 +18730,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-diff/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-docblock": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
@ -18329,6 +18777,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-each/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-environment-node": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
@ -18445,6 +18910,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-matcher-utils/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-message-util": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
@ -18466,6 +18948,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-message-util/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-message-util/node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@ -18540,6 +19039,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-resolve/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-resolve/node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@ -18583,6 +19099,36 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-runner/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-runner/node_modules/chalk/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/jest-runner/node_modules/jest-worker": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
@ -18710,6 +19256,23 @@
"concat-map": "0.0.1"
}
},
"node_modules/jest-runtime/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-runtime/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@ -18797,6 +19360,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-snapshot/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-util": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
@ -18815,6 +19395,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-util/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-util/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@ -18859,6 +19456,23 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/jest-validate/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-watcher": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
@ -18879,6 +19493,23 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-watcher/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jest-worker": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
@ -19847,6 +20478,22 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/log-symbols/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/log-update": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
@ -21355,6 +22002,23 @@
}
}
},
"node_modules/nx/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/nx/node_modules/cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@ -21735,6 +22399,22 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ora/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/ora/node_modules/cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@ -25740,6 +26420,23 @@
"webpack": "^5.0.0"
}
},
"node_modules/ts-loader/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
@ -25834,6 +26531,23 @@
"node": ">=10.13.0"
}
},
"node_modules/tsconfig-paths-webpack-plugin/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",

View File

@ -56,6 +56,7 @@
"@swc/helpers": "~0.5.11",
"@types/fabric": "^5.3.9",
"@types/node": "~18.16.9",
"chalk": "^5.4.1",
"concurrently": "^9.1.2",
"drizzle-kit": "^0.30.2",
"drizzle-orm": "^0.38.4",

View File

@ -50,6 +50,7 @@ import { PopoverService } from './services/popover.service';
[title]="popoverTitle"
[message]="popoverMessage"
[showInput]="popoverShowInput"
[showCancel]="popoverShowCancel"
[inputValue]="popoverInputValue"
[confirmText]="popoverConfirmText"
(confirmed)="handleConfirm($event)"
@ -103,6 +104,7 @@ export class AppComponent {
popoverTitle = '';
popoverMessage = '';
popoverShowInput = false;
popoverShowCancel = true;
popoverInputValue = '';
popoverConfirmText = 'Confirm';
private confirmCallback?: (inputValue?: string) => void;
@ -114,6 +116,7 @@ export class AppComponent {
this.popoverTitle = options.title;
this.popoverMessage = options.message;
this.popoverShowInput = options.showInput;
this.popoverShowCancel = options.showCancel;
this.popoverInputValue = options.inputValue;
this.popoverConfirmText = options.confirmText;
this.confirmCallback = options.onConfirm;

View File

@ -17,7 +17,9 @@ import { FormsModule } from '@angular/forms';
<input *ngIf="showInput" type="text" class="w-full p-2 border rounded mb-4 focus:ring-2 focus:ring-blue-500 focus:border-transparent" [(ngModel)]="inputValue" (keyup.enter)="onConfirm()" autofocus />
<div class="flex justify-end space-x-2">
@if(showCancel){
<button (click)="onCancel()" class="px-4 py-2 rounded bg-gray-100 text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400">Cancel</button>
}
<button (click)="onConfirm()" class="px-4 py-2 rounded bg-blue-500 text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500">
{{ confirmText }}
</button>
@ -30,6 +32,7 @@ export class PopoverComponent {
@Input() title: string = '';
@Input() message: string = '';
@Input() showInput: boolean = false;
@Input() showCancel: boolean = false;
@Input() confirmText: string = 'Confirm';
@Input() inputValue: string = '';
@Input() visible: boolean = false;

View File

@ -67,7 +67,7 @@
<div class="flex items-center space-x-2">
<div class="relative group">
<div class="absolute left-0 bottom-full mb-2 w-48 bg-white border border-gray-300 rounded shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-10 pointer-events-none">
<img src="/debug_images/{{ image.id }}/thumbnail.jpg" alt="{{ image.name }}" class="w-full h-auto object-cover" />
<img src="/debug_images/{{ image.bildid }}/thumbnail.jpg" alt="{{ image.name }}" class="w-full h-auto object-cover" />
</div>
<span class="font-medium cursor-pointer">{{ image.name }}</span>
</div>
@ -98,7 +98,7 @@
<div class="flex-1">
<div class="relative">
<label for="imageFile" class="flex justify-center items-center bg-green-500 text-white py-2 px-4 rounded hover:bg-green-600 cursor-pointer"> Add Image </label>
<input #imageFile type="file" id="imageFile" (change)="onFileChange($event)" accept="image/*" required class="hidden" />
<input #imageFile type="file" id="imageFile" (change)="onFileChange($event)" accept="image/jpeg,image/png,image/gif,image/webp" required class="hidden" />
</div>
</div>
</div>

View File

@ -144,18 +144,19 @@ export class DeckListComponent implements OnInit {
title: 'Delete Image',
message: `Are you sure you want to delete the image ${image.name}?`,
confirmText: 'Delete',
showCancel: true,
onConfirm: () => this.confirmImageDelete(deck, image),
});
}
confirmImageDelete(deck: Deck, image: DeckImage): void {
const imageId = image.id;
const imageId = image.bildid;
this.deckService.deleteImage(imageId).subscribe({
next: () => {
this.loadDecks();
if (this.activeDeck) {
this.activeDeck.images = this.activeDeck.images.filter(img => img.id !== imageId);
this.activeDeck.images = this.activeDeck.images.filter(img => img.bildid !== imageId);
this.cdr.detectChanges();
}
},
@ -166,7 +167,7 @@ export class DeckListComponent implements OnInit {
// Method to edit an image in a deck
editImage(deck: Deck, image: DeckImage): void {
let imageSrc = null;
fetch(`/debug_images/${image.id}/original_compressed.jpg`)
fetch(`/debug_images/${image.bildid}/original_compressed.jpg`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
@ -282,11 +283,36 @@ export class DeckListComponent implements OnInit {
onFileChange(event: any): void {
const file: File = event.target.files[0];
if (!file) return;
// Erlaubte Dateitypen
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
// Prüfe den Dateityp
if (!allowedTypes.includes(file.type)) {
this.popoverService.show({
title: 'Information',
message: 'Only JPG, PNG, GIF and WebP images are allowed',
});
this.resetFileInput();
return;
}
// Prüfe die Dateigröße (5MB = 5 * 1024 * 1024 Bytes)
const maxSize = 5 * 1024 * 1024; // 5MB in Bytes
if (file.size > maxSize) {
this.popoverService.show({
title: 'Information',
message: 'Image file size must not exceed 5MB',
});
this.resetFileInput();
return;
}
const fileNameElement = document.getElementById('fileName');
if (fileNameElement) {
fileNameElement.textContent = file.name;
}
// this.imageFile = file;
this.loading = true;
const reader = new FileReader();
@ -319,10 +345,9 @@ export class DeckListComponent implements OnInit {
const xMax = Math.max(...xs);
const yMin = Math.min(...ys);
const yMax = Math.max(...ys);
boxes.push({ x1: xMin, x2: xMax, y1: yMin, y2: yMax });
boxes.push({ x1: xMin, x2: xMax, y1: yMin, y2: yMax, inserted: null, updated: null });
});
const deckImage: DeckImage = { name: imageName, id: imageId, boxes };
//this.imageUploaded.emit({ imageSrc, deckImage });
const deckImage: DeckImage = { name: imageName, bildid: imageId, boxes };
this.imageData = { imageSrc, deckImage };
this.resetFileInput();
@ -333,6 +358,7 @@ export class DeckListComponent implements OnInit {
};
reader.readAsDataURL(file);
}
/**
* Resets the file input field so the same file can be selected again.
*/

View File

@ -10,8 +10,7 @@ export interface Deck {
export interface DeckImage {
boxes: Box[];
name: string;
//bildid: string;
id: string;
bildid: string;
}
export interface Box {
@ -26,12 +25,13 @@ export interface Box {
reps?: number;
lapses?: number;
isGraduated?: boolean;
inserted: string;
updated: string;
}
export interface BackendBox {
bildname: string;
deckid: number;
iconindex: number;
id: number;
x1: number;
x2: number;
@ -76,15 +76,15 @@ export class DeckService {
const imageMap: { [key: string]: DeckImage } = {};
images.forEach(image => {
if (!imageMap[image.id]) {
imageMap[image.id] = {
if (!imageMap[image.bildid]) {
imageMap[image.bildid] = {
name: image.name,
id: image.id,
bildid: image.bildid,
boxes: [],
};
}
imageMap[image.id].boxes.push({
id: image.boxid,
imageMap[image.bildid].boxes.push({
id: image.id,
x1: image.x1,
x2: image.x2,
y1: image.y1,
@ -95,6 +95,8 @@ export class DeckService {
reps: image.reps,
lapses: image.lapses,
isGraduated: image.isGraduated ? true : false,
inserted: image.inserted,
updated: image.updated,
});
});

View File

@ -24,7 +24,7 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
@ViewChild('canvas') canvasElement!: ElementRef<HTMLCanvasElement>;
detectedText: string = '';
boxes: { x1: number; x2: number; y1: number; y2: number }[] = [];
boxes: { x1: number; x2: number; y1: number; y2: number; id: number; inserted: string; updated: string }[] = [];
canvas!: fabric.Canvas;
maxCanvasWidth: number = 0;
@ -133,6 +133,7 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
hasControls: true,
hasBorders: true,
objectCaching: false,
data: { id: box.id, inserted: box.inserted, updated: box.updated },
});
rect.on('modified', () => {
@ -155,7 +156,6 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
});
this.updateBoxCoordinates();
// this.detectedText = ocrResults.map(result => result.text).join('\n');
} catch (error) {
console.error('Error processing image:', error);
@ -198,6 +198,9 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
x2: Math.round(x2),
y1: Math.round(y1),
y2: Math.round(y2),
id: rect.data?.id,
inserted: rect.data?.inserted,
updated: rect.data?.updated,
});
});
@ -226,7 +229,6 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
hasBorders: true,
objectCaching: false,
});
rect.on('modified', () => {
this.updateBoxCoordinates();
});
@ -256,7 +258,7 @@ export class EditImageModalComponent implements AfterViewInit, OnDestroy {
const data = {
deckname: this.deckName,
bildname: this.imageData.deckImage?.name, // this.imageFile?.name,
bildid: this.imageData.deckImage?.id,
bildid: this.imageData.deckImage?.bildid,
boxes: this.boxes,
};
this.deckService.saveImageData(data).subscribe({

View File

@ -26,7 +26,7 @@ export class MoveImageModalComponent {
return;
}
this.deckService.moveImage(this.image.id, this.selectedDeckId).subscribe({
this.deckService.moveImage(this.image.bildid, this.selectedDeckId).subscribe({
next: () => {
this.moveCompleted.emit();
this.close();

View File

@ -8,19 +8,22 @@ export class PopoverService {
title: string;
message: string;
showInput: boolean;
showCancel: boolean;
inputValue: string;
confirmText: string;
onConfirm: () => void;
onCancel?: () => void;
}>();
popoverState$ = this.showPopoverSource.asObservable();
show(options: { title: string; message: string; confirmText?: string; onConfirm?: (inputValue?: string) => void; onCancel?: () => void }) {
show(options: { title: string; message: string; confirmText?: string; showCancel?: boolean; onConfirm?: (inputValue?: string) => void; onCancel?: () => void }) {
this.showPopoverSource.next({
showInput: false,
inputValue: null,
confirmText: 'Ok',
showCancel: false,
onConfirm: (inputValue?: string) => {},
...options,
});
@ -28,6 +31,7 @@ export class PopoverService {
showWithInput(options: { title: string; message: string; confirmText: string; inputValue: string; onConfirm: (inputValue?: string) => void; onCancel?: () => void }) {
this.showPopoverSource.next({
showInput: true,
showCancel: true,
...options,
});
}

View File

@ -125,7 +125,7 @@ export class TrainingComponent implements OnInit {
if (!ctx || !this.currentImageData) return;
const img = new Image();
img.src = `/debug_images/${this.currentImageData.id}/original_compressed.jpg`;
img.src = `/debug_images/${this.currentImageData.bildid}/original_compressed.jpg`;
img.onload = () => {
// Set the canvas size to the image size
canvas.width = img.width;

View File

@ -17,10 +17,7 @@
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"lib": [
"ES2022",
"dom"
]
"lib": ["ES2022", "dom"]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,