catchup #13

Merged
toniwalter.green merged 16 commits from devel into main 2026-09-15 01:47:26 +02:00
23 changed files with 526 additions and 137 deletions
Showing only changes of commit 29c6828dd1 - Show all commits
+2
View File
@@ -10,3 +10,5 @@ src/cert.pem
src/key.pem src/key.pem
storage storage
src/services/push/vapid.json
+1 -1
View File
@@ -13,4 +13,4 @@ GRANT DELETE, INSERT, SELECT, UPDATE ON TABLES TO webapi;
ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public
GRANT USAGE ON TYPES TO webapi; GRANT USAGE ON TYPES TO webapi;
--GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO webapi;
+4 -2
View File
@@ -8,7 +8,7 @@
"postinstall": "playwright install" "postinstall": "playwright install"
}, },
"type": "module", "type": "module",
"packageManager": "pnpm@10.31.0+sha512.e3927388bfaa8078ceb79b748ffc1e8274e84d75163e67bc22e06c0d3aed43dd153151cbf11d7f8301ff4acb98c68bdc5cadf6989532801ffafe3b3e4a63c268", "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@types/connect-pg-simple": "^7.0.3", "@types/connect-pg-simple": "^7.0.3",
@@ -17,6 +17,7 @@
"@types/express-session": "^1.18.2", "@types/express-session": "^1.18.2",
"@types/node": "^25.3.5", "@types/node": "^25.3.5",
"@types/pg": "^8.18.0", "@types/pg": "^8.18.0",
"@types/web-push": "^3.6.4",
"prisma": "^7.4.2", "prisma": "^7.4.2",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "~5.9.3" "typescript": "~5.9.3"
@@ -37,6 +38,7 @@
"pg": "^8.20.0", "pg": "^8.20.0",
"playwright": "^1.58.2", "playwright": "^1.58.2",
"rxjs": "~7.8.2", "rxjs": "~7.8.2",
"tsoa": "^6.6.0" "tsoa": "^6.6.0",
"web-push": "^3.6.7"
} }
} }
@@ -0,0 +1,15 @@
-- CreateTable
CREATE TABLE "PushSubscription" (
"id" UUID NOT NULL DEFAULT uuidv7(),
"endpoint" VARCHAR NOT NULL,
"expirationTime" DOUBLE PRECISION,
"userSubscriptionId" UUID,
"p256dh" VARCHAR NOT NULL,
"auth" VARCHAR NOT NULL,
"userId" VARCHAR NOT NULL,
CONSTRAINT "PushSubscription_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "PushSubscription_userSubscriptionId_key" ON "PushSubscription"("userSubscriptionId");
@@ -0,0 +1,26 @@
/*
Warnings:
- You are about to drop the column `userSubscriptionId` on the `PushSubscription` table. All the data in the column will be lost.
- Added the required column `clientId` to the `PushSubscription` table without a default value. This is not possible if the table is not empty.
- Added the required column `topic` to the `PushSubscription` table without a default value. This is not possible if the table is not empty.
*/
-- DropIndex
DROP INDEX "PushSubscription_userSubscriptionId_key";
-- AlterTable
ALTER TABLE "PushSubscription" DROP COLUMN "userSubscriptionId",
ADD COLUMN "clientId" UUID NOT NULL,
ADD COLUMN "topic" "Topic" NOT NULL,
ADD COLUMN "topicConfiguration" JSONB;
-- CreateTable
CREATE TABLE "User" (
"id" VARCHAR NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "PushSubscription" ADD CONSTRAINT "PushSubscription_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+29 -8
View File
@@ -21,7 +21,7 @@ model Member {
kontakt String? @db.VarChar kontakt String? @db.VarChar
kuendigungzum DateTime? @db.Date kuendigungzum DateTime? @db.Date
marker Boolean? marker Boolean?
creationDate DateTime? @db.Date @default(now()) creationDate DateTime? @default(now()) @db.Date
} }
model session { model session {
@@ -37,13 +37,13 @@ enum Topic {
} }
model FoundEvents { model FoundEvents {
id String @id @default(dbgenerated("uuidv7()")) @db.Uuid id String @id @default(dbgenerated("uuidv7()")) @db.Uuid
source String @db.VarChar source String @db.VarChar
url String @db.VarChar url String @db.VarChar
foundDate DateTime @db.Date @default(now()) foundDate DateTime @default(now()) @db.Date
eventDate String? @db.VarChar eventDate String? @db.VarChar
title String? @db.VarChar title String? @db.VarChar
description String? @db.VarChar description String? @db.VarChar
attachedFiles FoundEventAttachments[] attachedFiles FoundEventAttachments[]
} }
@@ -53,3 +53,24 @@ model FoundEventAttachments {
event FoundEvents @relation(fields: [eventId], references: [id]) event FoundEvents @relation(fields: [eventId], references: [id])
eventId String @db.Uuid eventId String @db.Uuid
} }
model PushSubscription {
id String @id @default(dbgenerated("uuidv7()")) @db.Uuid
endpoint String @db.VarChar
expirationTime Float?
p256dh String @db.VarChar
auth String @db.VarChar
userId String @db.VarChar
user User @relation(fields: [userId], references: [id])
topic Topic
topicConfiguration Json? @db.JsonB
clientId String @db.Uuid
}
model User {
id String @db.VarChar @id
subscriptions PushSubscription[]
}
@@ -0,0 +1,50 @@
import express from "express";
import { Body, Controller, Post, Query, Request, Route, Security, SuccessResponse } from "tsoa";
import type { PushSubscriptionCreateArgs as PushSubscriptionCreateArgsDto } from "../dtos/pushSubscription.js";
import { inject } from "../infrastructure/di/injector.js";
import { KC_SECURITY_NAME } from "../services/keycloak/user/keycloak-user.js";
import { PushService } from "../services/push/pushService.js";
@Route("push")
export class PushContoller extends Controller {
private readonly pushService = inject(PushService);
@Post("add")
@Security(KC_SECURITY_NAME)
@SuccessResponse("200", "OK")
public async addSubscription(
@Body() subscription: PushSubscriptionCreateArgsDto,
@Request() request: express.Request,
): Promise<void> {
return await this.pushService.storeSubscription(subscription, request, request.res);
}
@Post('reset')
@Security(KC_SECURITY_NAME)
@SuccessResponse('200', 'OK')
public async resetSubscriptions(
@Request() request: express.Request
): Promise<void>{
return await this.pushService.reset(request);
}
@Post('clearSingle')
@Security(KC_SECURITY_NAME)
@SuccessResponse("200", 'OK')
public async clearSubscription(
@Request() request: express.Request,
@Query() clientId: string
):Promise<void>{
await this.pushService.clearSubscription(clientId, request);
}
@Post('test-publish')
@Security(KC_SECURITY_NAME)
@SuccessResponse("200", "OK")
public async publishTestMessage(
@Request() request: express.Request
): Promise<void>{
this.pushService.sendtestNotification(request);
}
}
@@ -13,11 +13,11 @@ import { Claim } from "../dtos/claim.js";
import { type User } from "../dtos/user.js"; import { type User } from "../dtos/user.js";
import { inject } from "../infrastructure/di/index.js"; import { inject } from "../infrastructure/di/index.js";
import { KC_SECURITY_NAME } from "../services/keycloak/user/index.js"; import { KC_SECURITY_NAME } from "../services/keycloak/user/index.js";
import { UserService } from "../services/userService.js"; import { UserAdminService } from "../services/userAdminService.js";
@Route("users") @Route("users")
export class UsersController extends Controller { export class UsersController extends Controller {
private readonly userService = inject(UserService); private readonly userService = inject(UserAdminService);
@Security(KC_SECURITY_NAME, [Claim.UserAdmin]) @Security(KC_SECURITY_NAME, [Claim.UserAdmin])
@SuccessResponse("200", "OK") @SuccessResponse("200", "OK")
+24
View File
@@ -0,0 +1,24 @@
import type { JsonValue } from "@prisma/client/runtime/client";
export class PushSubscriptionDto{
id!: string;
endpoint!: string;
expirationTime!: number | null;
keys!: {
p256dh: string;
auth: string;
};
topic!: "TEST";
topicConfiguration!: JsonValue | null;
clientId!: string;
}
export class PushSubscriptionCreateArgs/* implements Omit<PushSubscriptionDto, "id">*/{
endpoint!: string;
expirationTime!: number | null;
keys!: { p256dh: string; auth: string; };
topic!: "TEST";
topicConfiguration!: string | null;
clientId!: string;
}
+12 -1
View File
@@ -1,6 +1,7 @@
import { PrismaPg } from "@prisma/adapter-pg"; import { PrismaPg } from "@prisma/adapter-pg";
import { type Request } from "express"; import { type Request } from "express";
import { PrismaClient } from "../../generated/prisma/client.js"; import { PrismaClient } from "../../generated/prisma/client.js";
import type { UserModel } from "../../generated/prisma/models.js";
import { Injectable } from "../../infrastructure/di/injectable-decorator.js"; import { Injectable } from "../../infrastructure/di/injectable-decorator.js";
type ExtendedClient = ReturnType<typeof createExtendedClient>; type ExtendedClient = ReturnType<typeof createExtendedClient>;
@@ -23,7 +24,17 @@ export type SafeClient = Omit<
>; >;
function createExtendedClient(client: PrismaClient) { function createExtendedClient(client: PrismaClient) {
return client; return client.$extends({
client: {
async ensureUserKnown(userId: string): Promise<UserModel>{
let user = await client.user.findUnique({where: {id: userId}});
if(user==null){
user = await client.user.create({data:{id: userId}});
}
return user
}
}
});
// .$extends({ // .$extends({
// name: "testExtension", // name: "testExtension",
// model: { // model: {
@@ -31,6 +31,9 @@ export class KeycloakUser {
this.keycloakConnect = new Promise(async resolve => { this.keycloakConnect = new Promise(async resolve => {
const config = await this.getConfig(); const config = await this.getConfig();
const client = new KeycloakConnect({ store: sessionStore }, config); const client = new KeycloakConnect({ store: sessionStore }, config);
client.authenticated=(r)=>{
console.dir(r);
};
resolve(client); resolve(client);
}); });
} }
@@ -49,4 +52,10 @@ export class KeycloakUser {
const client = await this.keycloakConnect; const client = await this.keycloakConnect;
return client.middleware(); return client.middleware();
} }
public async getUid(request: Request, response?: Response): Promise<string>{
const client = await this.keycloakConnect;
const grant = await client.getGrant(request, response ?? ({} as any));
return (grant.access_token as any)?.content?.sub;
}
} }
@@ -0,0 +1,106 @@
import { DbNull } from "@prisma/client/runtime/client";
import type { Request, Response } from "express";
import webpush from "web-push";
import type { PushSubscriptionCreateArgs } from "../../dtos/pushSubscription.js";
import { Topic } from "../../generated/prisma/enums.js";
import { inject } from "../../infrastructure/di/index.js";
import { Injectable } from "../../infrastructure/di/injectable-decorator.js";
import { DatabaseService } from "../db/prisma.js";
import { KeycloakUser } from "../keycloak/user/keycloak-user.js";
import VAPID from './vapid.json' with { type: "json" };
@Injectable()
export class PushService{
private readonly db = inject(DatabaseService);
private readonly userService = inject(KeycloakUser);
public constructor(){
webpush.setVapidDetails("mailto:toniwalter.blue@gmail.com", VAPID.publicKey, VAPID.privateKey);
}
public async storeSubscription(subscription: PushSubscriptionCreateArgs, request: Request, response?: Response): Promise<void>{
const userId= await this.userService.getUid(request, response);
await this.db.doRequest(async prisma=>{
await prisma.ensureUserKnown(userId);
return await prisma.pushSubscription.create({
data: {
auth: subscription.keys.auth,
endpoint: subscription.endpoint,
p256dh: subscription.keys.p256dh,
expirationTime: subscription.expirationTime ?? null,
userId: userId,
clientId: subscription.clientId,
topic: subscription.topic,
topicConfiguration: subscription.topicConfiguration ? JSON.parse(subscription.topicConfiguration) : DbNull,
}
});
}, request);
}
public async sendtestNotification(request: Request){
const subscriptions = await this.db.doRequest(async prisma=>prisma.pushSubscription.findMany({where:{topic: "TEST"}}), request);
const payload = {
notification: {
title: "ATitle",
body: "DatBod",
icon: "assets/some-icon.png",
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: 1
},
actions: [
{
action: "explode",
title: "Make Boom"
}
]
}
}
console.log(`sending ${subscriptions.length} test messages`);
for(const s of subscriptions){
const subscription: webpush.PushSubscription={
endpoint: s.endpoint,
keys: {
auth: s.auth,
p256dh: s.p256dh
},
expirationTime: s.expirationTime
};
try{
const result = await webpush.sendNotification(subscription, JSON.stringify(payload), {
topic: Topic.TEST,
});
console.log("success");
console.dir(result);
}catch(error){
console.error("Cannot send notification: ", error);
throw error;
}
}
}
public async reset(request: Request): Promise<void> {
const userId=await this.userService.getUid(request, request.res);
const subscriptions = await this.db.doRequest(async prisma=>await prisma.pushSubscription.deleteMany({
where:{userId: userId}
}), request);
console.log(`deleted ${subscriptions.count} subs for ${userId}`);
}
public async clearSubscription(clientId: string, request: Request): Promise<number> {
const userId =await this.userService.getUid(request, request.res);
const batchResult =await this.db.doRequest(async prisma=>await prisma.pushSubscription.deleteMany({
where: {
userId: userId,
AND: {
clientId: clientId
}
}
}), request);
return batchResult.count;
}
}
@@ -3,7 +3,7 @@ import { type User } from "../dtos/user.js";
import { inject } from "../infrastructure/di/index.js"; import { inject } from "../infrastructure/di/index.js";
import { KeycloakAdmin } from "./keycloak/admin/index.js"; import { KeycloakAdmin } from "./keycloak/admin/index.js";
export class UserService { export class UserAdminService {
private readonly keycloakAdmin = inject(KeycloakAdmin); private readonly keycloakAdmin = inject(KeycloakAdmin);
public async setClaim( public async setClaim(
uid: string, uid: string,
+3 -1
View File
@@ -54,7 +54,9 @@
"strictPropertyInitialization": true, "strictPropertyInitialization": true,
"noImplicitThis": true, "noImplicitThis": true,
"alwaysStrict": true, "alwaysStrict": true,
"forceConsistentCasingInFileNames": true "forceConsistentCasingInFileNames": true,
"preserveWatchOutput": true,
"esModuleInterop": true
}, },
"files": ["./src/index.ts"], "files": ["./src/index.ts"],
"include": ["src", "./package.json"] "include": ["src", "./package.json"]
+2
View File
@@ -44,3 +44,5 @@ Thumbs.db
### ###
src/app/generated-api/ src/app/generated-api/
src/app/services/push/vapid.json
+85 -84
View File
@@ -1,91 +1,92 @@
{ {
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1, "version": 1,
"cli": { "cli": {
"packageManager": "pnpm" "packageManager": "pnpm"
}, },
"newProjectRoot": "projects", "newProjectRoot": "projects",
"projects": { "projects": {
"frontend": { "frontend": {
"projectType": "application", "projectType": "application",
"schematics": { "schematics": {
"@schematics/angular:component": { "@schematics/angular:component": {
"style": "scss" "style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"browser": "src/main.ts",
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": ["src/styles.scss"]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
} }
],
"outputHashing": "all",
"serviceWorker": "ngsw-config.json"
}, },
"development": { "root": "",
"optimization": false, "sourceRoot": "src",
"extractLicenses": false, "prefix": "app",
"sourceMap": true "architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"browser": "src/main.ts",
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": ["src/styles.scss"]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all",
"serviceWorker": "ngsw-config.json"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true,
"serviceWorker": "ngsw-config.json"
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "frontend:build:production"
},
"development": {
"buildTarget": "frontend:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular/build:extract-i18n"
},
"test": {
"builder": "@angular/build:karma",
"options": {
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": ["src/styles.scss"]
}
}
} }
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "frontend:build:production"
},
"development": {
"buildTarget": "frontend:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular/build:extract-i18n"
},
"test": {
"builder": "@angular/build:karma",
"options": {
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": ["src/styles.scss"]
}
} }
}
} }
}
} }
+2 -1
View File
@@ -42,7 +42,8 @@
"keycloak-angular": "^21.0.0", "keycloak-angular": "^21.0.0",
"keycloak-js": "^26.2.3", "keycloak-js": "^26.2.3",
"rxjs": "^7.8.2", "rxjs": "^7.8.2",
"tslib": "^2.8.1" "tslib": "^2.8.1",
"uuid": "^13.0.0"
}, },
"devDependencies": { "devDependencies": {
"@angular/build": "^21.2.1", "@angular/build": "^21.2.1",
+1 -1
View File
@@ -86,7 +86,7 @@ export const appConfig: ApplicationConfig = {
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS }, { provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS },
{ provide: MAT_DATE_LOCALE, useValue: de }, { provide: MAT_DATE_LOCALE, useValue: de },
provideServiceWorker('ngsw-worker.js', { provideServiceWorker('ngsw-worker.js', {
enabled: !isDevMode(), enabled: true,//!isDevMode(),
registrationStrategy: 'registerWhenStable:30000', registrationStrategy: 'registerWhenStable:30000',
}), }),
], ],
@@ -1,6 +1,9 @@
<h1>Testeite für interne Berechtigung</h1> <h1>Testeite für interne Berechtigung</h1>
<p>API: {{apiStatus()}}</p> <p>API: {{apiStatus()}}</p>
<p>Auth: {{authTest()}}</p> <p>Auth: {{authTest()}}</p>
<button (click)="pushService.subscribeToEvents()">Subscribe</button>
<button (click)="pushService.resetSubscriptions()">Unsubscribe</button>
<button (click)="pushService.publishTestMessage()">TestMessage</button>
Claim: Claim:
<ul> <ul>
@let claims = this.auth.claims(); @let claims = this.auth.claims();
@@ -8,6 +8,7 @@ import {
} from '@angular/core'; } from '@angular/core';
import * as api from '../../generated-api/api'; import * as api from '../../generated-api/api';
import { Authentication } from '../../services/authentication'; import { Authentication } from '../../services/authentication';
import { PushService } from '../../services/push/pushService';
@Component({ @Component({
selector: 'app-test', selector: 'app-test',
@@ -17,6 +18,7 @@ import { Authentication } from '../../services/authentication';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class Test { export class Test {
protected readonly pushService = inject(PushService);
protected auth = inject(Authentication); protected auth = inject(Authentication);
protected apiStatus = signal<string>('Waiting'); protected apiStatus = signal<string>('Waiting');
@@ -0,0 +1,70 @@
import { inject, Injectable } from '@angular/core';
import { SwPush } from '@angular/service-worker';
import { v7 as uuidv7 } from 'uuid';
import * as api from '../../generated-api/api';
import VAPID from './vapid.json' with { type: "json" };
const SUBSCRIPTION_CLIENT_ID="subscriptionClientId";
@Injectable({providedIn: 'root'})
export class PushService{
private readonly swPush=inject(SwPush);
private arrayBufferToString(buffer: ArrayBuffer|null){
if(buffer==null){
return '';
}
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.length;
for(let i = 0; i< len; i++){
binary+=String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
public async subscribeToEvents(){
try{
const subscription = await this.swPush.requestSubscription({
serverPublicKey: VAPID.publicKey
});
const decoder = new TextDecoder();
const previousCliendId = localStorage.getItem(SUBSCRIPTION_CLIENT_ID);
if(previousCliendId){
try{
await api.clearSubscription(previousCliendId);
console.log("previous subscription cleared");
}catch{}
}
const newClientId= uuidv7();
await api.addSubscription({
endpoint: subscription.endpoint,
keys: {
auth: this.arrayBufferToString(subscription.getKey('auth')),
p256dh: this.arrayBufferToString(subscription.getKey('p256dh')),
},
expirationTime: subscription.expirationTime,
topic: api.Topic.Test,
topicConfiguration: null,
clientId: newClientId,
});
localStorage.setItem(SUBSCRIPTION_CLIENT_ID, newClientId);
}catch(error){
console.error("Subscribing failed: ", error);
}
}
public async resetSubscriptions(){
await this.swPush.unsubscribe();
await api.resetSubscriptions();
}
public async publishTestMessage(){
await api.publishTestMessage();
}
}
+76 -34
View File
@@ -65,6 +65,9 @@ importers:
tsoa: tsoa:
specifier: ^6.6.0 specifier: ^6.6.0
version: 6.6.0 version: 6.6.0
web-push:
specifier: ^3.6.7
version: 3.6.7
devDependencies: devDependencies:
'@types/connect-pg-simple': '@types/connect-pg-simple':
specifier: ^7.0.3 specifier: ^7.0.3
@@ -84,6 +87,9 @@ importers:
'@types/pg': '@types/pg':
specifier: ^8.18.0 specifier: ^8.18.0
version: 8.18.0 version: 8.18.0
'@types/web-push':
specifier: ^3.6.4
version: 3.6.4
prisma: prisma:
specifier: ^7.4.2 specifier: ^7.4.2
version: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) version: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
@@ -150,6 +156,9 @@ importers:
tslib: tslib:
specifier: ^2.8.1 specifier: ^2.8.1
version: 2.8.1 version: 2.8.1
uuid:
specifier: ^13.0.0
version: 13.0.0
devDependencies: devDependencies:
'@angular/build': '@angular/build':
specifier: ^21.2.1 specifier: ^21.2.1
@@ -1264,49 +1273,42 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@napi-rs/nice-linux-arm64-musl@1.1.1': '@napi-rs/nice-linux-arm64-musl@1.1.1':
resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==} resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@napi-rs/nice-linux-ppc64-gnu@1.1.1': '@napi-rs/nice-linux-ppc64-gnu@1.1.1':
resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==} resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@napi-rs/nice-linux-riscv64-gnu@1.1.1': '@napi-rs/nice-linux-riscv64-gnu@1.1.1':
resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==} resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@napi-rs/nice-linux-s390x-gnu@1.1.1': '@napi-rs/nice-linux-s390x-gnu@1.1.1':
resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==} resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@napi-rs/nice-linux-x64-gnu@1.1.1': '@napi-rs/nice-linux-x64-gnu@1.1.1':
resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==} resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@napi-rs/nice-linux-x64-musl@1.1.1': '@napi-rs/nice-linux-x64-musl@1.1.1':
resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==} resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@napi-rs/nice-openharmony-arm64@1.1.1': '@napi-rs/nice-openharmony-arm64@1.1.1':
resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==} resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==}
@@ -1414,42 +1416,36 @@ packages:
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm-musl@2.5.6': '@parcel/watcher-linux-arm-musl@2.5.6':
resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [musl]
'@parcel/watcher-linux-arm64-glibc@2.5.6': '@parcel/watcher-linux-arm64-glibc@2.5.6':
resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm64-musl@2.5.6': '@parcel/watcher-linux-arm64-musl@2.5.6':
resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@parcel/watcher-linux-x64-glibc@2.5.6': '@parcel/watcher-linux-x64-glibc@2.5.6':
resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@parcel/watcher-linux-x64-musl@2.5.6': '@parcel/watcher-linux-x64-musl@2.5.6':
resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@parcel/watcher-win32-arm64@2.5.6': '@parcel/watcher-win32-arm64@2.5.6':
resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==}
@@ -1570,28 +1566,24 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.4': '@rolldown/binding-linux-arm64-musl@1.0.0-rc.4':
resolution: {integrity: sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==} resolution: {integrity: sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.4': '@rolldown/binding-linux-x64-gnu@1.0.0-rc.4':
resolution: {integrity: sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==} resolution: {integrity: sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.0.0-rc.4': '@rolldown/binding-linux-x64-musl@1.0.0-rc.4':
resolution: {integrity: sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==} resolution: {integrity: sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rolldown/binding-openharmony-arm64@1.0.0-rc.4': '@rolldown/binding-openharmony-arm64@1.0.0-rc.4':
resolution: {integrity: sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==} resolution: {integrity: sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==}
@@ -1653,79 +1645,66 @@ packages:
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.59.0': '@rollup/rollup-linux-arm-musleabihf@4.59.0':
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.59.0': '@rollup/rollup-linux-arm64-gnu@4.59.0':
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.59.0': '@rollup/rollup-linux-arm64-musl@4.59.0':
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.59.0': '@rollup/rollup-linux-loong64-gnu@4.59.0':
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.59.0': '@rollup/rollup-linux-loong64-musl@4.59.0':
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.59.0': '@rollup/rollup-linux-ppc64-gnu@4.59.0':
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.59.0': '@rollup/rollup-linux-ppc64-musl@4.59.0':
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.59.0': '@rollup/rollup-linux-riscv64-gnu@4.59.0':
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.59.0': '@rollup/rollup-linux-riscv64-musl@4.59.0':
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.59.0': '@rollup/rollup-linux-s390x-gnu@4.59.0':
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.59.0': '@rollup/rollup-linux-x64-gnu@4.59.0':
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.59.0': '@rollup/rollup-linux-x64-musl@4.59.0':
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.59.0': '@rollup/rollup-openbsd-x64@4.59.0':
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
@@ -1940,6 +1919,9 @@ packages:
'@types/tough-cookie@4.0.5': '@types/tough-cookie@4.0.5':
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
'@types/web-push@3.6.4':
resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==}
'@types/yauzl@2.10.3': '@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
@@ -2135,6 +2117,9 @@ packages:
buffer-crc32@0.2.13: buffer-crc32@0.2.13:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
buffer-equal-constant-time@1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
buffer-from@1.1.2: buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
@@ -2236,8 +2221,8 @@ packages:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'} engines: {node: '>=18'}
chromedriver@146.0.1: chromedriver@146.0.2:
resolution: {integrity: sha512-XT1FnStrsGAS6l3M0yLtLjqSZWcOK0cBoR/QKLzA0rvf4oGtxnxYtCv6G0j8/0xNZTY2Zv9GOARZ5uBNc3bjTw==} resolution: {integrity: sha512-/A6ht59pGGrV3bU6eC//yH6W+NRexVGXy/KEe+pNn1MP5Xb34krSA02bGlYuA5XCrfdXPsFI//slvsOBwH//4Q==}
engines: {node: '>=20'} engines: {node: '>=20'}
hasBin: true hasBin: true
@@ -2548,6 +2533,9 @@ packages:
eastasianwidth@0.2.0: eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
ee-first@1.1.1: ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
@@ -3048,6 +3036,10 @@ packages:
resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==}
engines: {node: '>=10.19.0'} engines: {node: '>=10.19.0'}
http_ece@1.2.0:
resolution: {integrity: sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==}
engines: {node: '>=16'}
https-proxy-agent@7.0.6: https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
@@ -3295,9 +3287,15 @@ packages:
resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
engines: {'0': node >= 0.2.0} engines: {'0': node >= 0.2.0}
jwa@2.0.1:
resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==}
jwk-to-pem@2.0.7: jwk-to-pem@2.0.7:
resolution: {integrity: sha512-cSVphrmWr6reVchuKQZdfSs4U9c5Y4hwZggPoz6cbVnTpAVgGRpEuQng86IyqLeGZlhTh+c4MAreB6KbdQDKHQ==} resolution: {integrity: sha512-cSVphrmWr6reVchuKQZdfSs4U9c5Y4hwZggPoz6cbVnTpAVgGRpEuQng86IyqLeGZlhTh+c4MAreB6KbdQDKHQ==}
jws@4.0.1:
resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==}
karma-chrome-launcher@3.2.0: karma-chrome-launcher@3.2.0:
resolution: {integrity: sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==} resolution: {integrity: sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==}
@@ -4626,6 +4624,10 @@ packages:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'} engines: {node: '>= 0.4.0'}
uuid@13.0.0:
resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==}
hasBin: true
vali-date@1.0.0: vali-date@1.0.0:
resolution: {integrity: sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==} resolution: {integrity: sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -4708,6 +4710,11 @@ packages:
weak-lru-cache@1.2.2: weak-lru-cache@1.2.2:
resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==}
web-push@3.6.7:
resolution: {integrity: sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==}
engines: {node: '>= 16'}
hasBin: true
webidl-conversions@7.0.0: webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'} engines: {node: '>=12'}
@@ -6758,6 +6765,10 @@ snapshots:
'@types/tough-cookie@4.0.5': {} '@types/tough-cookie@4.0.5': {}
'@types/web-push@3.6.4':
dependencies:
'@types/node': 25.3.5
'@types/yauzl@2.10.3': '@types/yauzl@2.10.3':
dependencies: dependencies:
'@types/node': 25.3.5 '@types/node': 25.3.5
@@ -6976,6 +6987,8 @@ snapshots:
buffer-crc32@0.2.13: buffer-crc32@0.2.13:
optional: true optional: true
buffer-equal-constant-time@1.0.1: {}
buffer-from@1.1.2: {} buffer-from@1.1.2: {}
buffer@5.7.1: buffer@5.7.1:
@@ -7115,7 +7128,7 @@ snapshots:
chownr@3.0.0: {} chownr@3.0.0: {}
chromedriver@146.0.1: chromedriver@146.0.2:
dependencies: dependencies:
'@testim/chrome-version': 1.1.4 '@testim/chrome-version': 1.1.4
axios: 1.13.6 axios: 1.13.6
@@ -7418,6 +7431,10 @@ snapshots:
eastasianwidth@0.2.0: {} eastasianwidth@0.2.0: {}
ecdsa-sig-formatter@1.0.11:
dependencies:
safe-buffer: 5.2.1
ee-first@1.1.1: {} ee-first@1.1.1: {}
effect@3.18.4: effect@3.18.4:
@@ -8079,6 +8096,8 @@ snapshots:
quick-lru: 5.1.1 quick-lru: 5.1.1
resolve-alpn: 1.2.1 resolve-alpn: 1.2.1
http_ece@1.2.0: {}
https-proxy-agent@7.0.6: https-proxy-agent@7.0.6:
dependencies: dependencies:
agent-base: 7.1.4 agent-base: 7.1.4
@@ -8345,12 +8364,23 @@ snapshots:
jsonparse@1.3.1: {} jsonparse@1.3.1: {}
jwa@2.0.1:
dependencies:
buffer-equal-constant-time: 1.0.1
ecdsa-sig-formatter: 1.0.11
safe-buffer: 5.2.1
jwk-to-pem@2.0.7: jwk-to-pem@2.0.7:
dependencies: dependencies:
asn1.js: 5.4.1 asn1.js: 5.4.1
elliptic: 6.6.1 elliptic: 6.6.1
safe-buffer: 5.2.1 safe-buffer: 5.2.1
jws@4.0.1:
dependencies:
jwa: 2.0.1
safe-buffer: 5.2.1
karma-chrome-launcher@3.2.0: karma-chrome-launcher@3.2.0:
dependencies: dependencies:
which: 1.3.1 which: 1.3.1
@@ -8422,7 +8452,7 @@ snapshots:
dependencies: dependencies:
jwk-to-pem: 2.0.7 jwk-to-pem: 2.0.7
optionalDependencies: optionalDependencies:
chromedriver: 146.0.1 chromedriver: 146.0.2
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
- supports-color - supports-color
@@ -9849,6 +9879,8 @@ snapshots:
utils-merge@1.0.1: {} utils-merge@1.0.1: {}
uuid@13.0.0: {}
vali-date@1.0.0: {} vali-date@1.0.0: {}
valibot@1.2.0(typescript@5.9.3): valibot@1.2.0(typescript@5.9.3):
@@ -9895,6 +9927,16 @@ snapshots:
weak-lru-cache@1.2.2: weak-lru-cache@1.2.2:
optional: true optional: true
web-push@3.6.7:
dependencies:
asn1.js: 5.4.1
http_ece: 1.2.0
https-proxy-agent: 7.0.6
jws: 4.0.1
minimist: 1.2.8
transitivePeerDependencies:
- supports-color
webidl-conversions@7.0.0: {} webidl-conversions@7.0.0: {}
whatwg-encoding@3.1.1: whatwg-encoding@3.1.1:
+1 -1
View File
@@ -3,4 +3,4 @@ $env:TKD_DB_HOST="localhost"
$env:TKD_DB_USER="webapi" $env:TKD_DB_USER="webapi"
$env:TKD_DB_PASSWORD="devpassword" $env:TKD_DB_PASSWORD="devpassword"
$env:TKD_COOKIE_SECRET="devsecret" $env:TKD_COOKIE_SECRET="devsecret"
pnpx tsx --watch --tsconfig .\tsconfig.json --clear-screen=false .\src\index.ts pnpx tsx --watch --tsconfig .\tsconfig.json .\src\index.ts