From 2ee7d402b6f95428bb34330cdb8bb79531f0cae0 Mon Sep 17 00:00:00 2001 From: Lorenzo Sanesi Date: Thu, 23 Jul 2026 19:34:00 +0200 Subject: [PATCH] Add scouthub-attivit-be --- scouthub-attivita-be/.dockerignore | 7 + scouthub-attivita-be/.env.example | 13 + scouthub-attivita-be/.gitignore | 9 + scouthub-attivita-be/DDL/1-creazioneDB.sql | 240 + .../DDL/2-insertStatoAttivita.sql | 3 + .../DDL/3-insertTipoCategoria.sql | 2 + .../DDL/4-insertTipoParagrafo.sql | 2 + scouthub-attivita-be/DDL/5-insertBranca.sql | 4 + .../DDL/6-insertPeriodoAnno.sql | 3 + .../DDL/7-insertCategoria.sql | 9 + .../DDL/8-insertMateriale.sql | 0 scouthub-attivita-be/DDL/insertScouthub.xlsx | Bin 0 -> 17518 bytes scouthub-attivita-be/Dockerfile | 16 + scouthub-attivita-be/README.md | 53 + scouthub-attivita-be/jest.config.ts | 16 + scouthub-attivita-be/package-lock.json | 6908 +++++++++++++++++ scouthub-attivita-be/package.json | 47 + scouthub-attivita-be/prisma/.gitkeep | 0 .../20260722231426_init/migration.sql | 223 + .../migration.sql | 7 + .../prisma/migrations/migration_lock.toml | 3 + scouthub-attivita-be/prisma/schema.prisma | 223 + scouthub-attivita-be/prisma/seed.ts | 114 + scouthub-attivita-be/src/app.ts | 30 + scouthub-attivita-be/src/config/env.ts | 21 + scouthub-attivita-be/src/db/prisma.ts | 12 + scouthub-attivita-be/src/errors.ts | 9 + scouthub-attivita-be/src/middlewares/.gitkeep | 0 .../src/middlewares/auth.types.ts | 6 + .../src/middlewares/authenticate.ts | 67 + .../src/middlewares/errorHandler.ts | 10 + .../src/modules/attivita/.gitkeep | 0 .../attivita/attivita.router.private.ts | 51 + .../attivita/attivita.router.public.ts | 63 + .../src/modules/attivita/attivita.service.ts | 560 ++ .../src/modules/autocomplete/.gitkeep | 0 .../autocomplete/autocomplete.router.ts | 56 + .../autocomplete/autocomplete.service.ts | 62 + scouthub-attivita-be/src/server.ts | 6 + scouthub-attivita-be/src/types/.gitkeep | 0 scouthub-attivita-be/src/types/dto.ts | 76 + scouthub-attivita-be/src/types/express.d.ts | 11 + scouthub-attivita-be/src/types/validation.ts | 58 + scouthub-attivita-be/tests/.gitkeep | 0 .../integration/attivita.endpoints.test.ts | 192 + .../autocomplete.endpoints.test.ts | 52 + .../tests/setup/authTestHelper.ts | 41 + .../tests/setup/globalSetup.ts | 26 + .../tests/setup/globalTeardown.ts | 5 + .../tests/setup/resetDatabase.ts | 18 + .../tests/unit/attivita.service.test.ts | 257 + .../tests/unit/autocomplete.service.test.ts | 89 + .../unit/middleware/authenticate.test.ts | 75 + scouthub-attivita-be/tsconfig.jest.json | 9 + scouthub-attivita-be/tsconfig.json | 19 + 55 files changed, 9783 insertions(+) create mode 100644 scouthub-attivita-be/.dockerignore create mode 100644 scouthub-attivita-be/.env.example create mode 100644 scouthub-attivita-be/.gitignore create mode 100644 scouthub-attivita-be/DDL/1-creazioneDB.sql create mode 100644 scouthub-attivita-be/DDL/2-insertStatoAttivita.sql create mode 100644 scouthub-attivita-be/DDL/3-insertTipoCategoria.sql create mode 100644 scouthub-attivita-be/DDL/4-insertTipoParagrafo.sql create mode 100644 scouthub-attivita-be/DDL/5-insertBranca.sql create mode 100644 scouthub-attivita-be/DDL/6-insertPeriodoAnno.sql create mode 100644 scouthub-attivita-be/DDL/7-insertCategoria.sql create mode 100644 scouthub-attivita-be/DDL/8-insertMateriale.sql create mode 100644 scouthub-attivita-be/DDL/insertScouthub.xlsx create mode 100644 scouthub-attivita-be/Dockerfile create mode 100644 scouthub-attivita-be/README.md create mode 100644 scouthub-attivita-be/jest.config.ts create mode 100644 scouthub-attivita-be/package-lock.json create mode 100644 scouthub-attivita-be/package.json create mode 100644 scouthub-attivita-be/prisma/.gitkeep create mode 100644 scouthub-attivita-be/prisma/migrations/20260722231426_init/migration.sql create mode 100644 scouthub-attivita-be/prisma/migrations/20260723130000_add_autore_id/migration.sql create mode 100644 scouthub-attivita-be/prisma/migrations/migration_lock.toml create mode 100644 scouthub-attivita-be/prisma/schema.prisma create mode 100644 scouthub-attivita-be/prisma/seed.ts create mode 100644 scouthub-attivita-be/src/app.ts create mode 100644 scouthub-attivita-be/src/config/env.ts create mode 100644 scouthub-attivita-be/src/db/prisma.ts create mode 100644 scouthub-attivita-be/src/errors.ts create mode 100644 scouthub-attivita-be/src/middlewares/.gitkeep create mode 100644 scouthub-attivita-be/src/middlewares/auth.types.ts create mode 100644 scouthub-attivita-be/src/middlewares/authenticate.ts create mode 100644 scouthub-attivita-be/src/middlewares/errorHandler.ts create mode 100644 scouthub-attivita-be/src/modules/attivita/.gitkeep create mode 100644 scouthub-attivita-be/src/modules/attivita/attivita.router.private.ts create mode 100644 scouthub-attivita-be/src/modules/attivita/attivita.router.public.ts create mode 100644 scouthub-attivita-be/src/modules/attivita/attivita.service.ts create mode 100644 scouthub-attivita-be/src/modules/autocomplete/.gitkeep create mode 100644 scouthub-attivita-be/src/modules/autocomplete/autocomplete.router.ts create mode 100644 scouthub-attivita-be/src/modules/autocomplete/autocomplete.service.ts create mode 100644 scouthub-attivita-be/src/server.ts create mode 100644 scouthub-attivita-be/src/types/.gitkeep create mode 100644 scouthub-attivita-be/src/types/dto.ts create mode 100644 scouthub-attivita-be/src/types/express.d.ts create mode 100644 scouthub-attivita-be/src/types/validation.ts create mode 100644 scouthub-attivita-be/tests/.gitkeep create mode 100644 scouthub-attivita-be/tests/integration/attivita.endpoints.test.ts create mode 100644 scouthub-attivita-be/tests/integration/autocomplete.endpoints.test.ts create mode 100644 scouthub-attivita-be/tests/setup/authTestHelper.ts create mode 100644 scouthub-attivita-be/tests/setup/globalSetup.ts create mode 100644 scouthub-attivita-be/tests/setup/globalTeardown.ts create mode 100644 scouthub-attivita-be/tests/setup/resetDatabase.ts create mode 100644 scouthub-attivita-be/tests/unit/attivita.service.test.ts create mode 100644 scouthub-attivita-be/tests/unit/autocomplete.service.test.ts create mode 100644 scouthub-attivita-be/tests/unit/middleware/authenticate.test.ts create mode 100644 scouthub-attivita-be/tsconfig.jest.json create mode 100644 scouthub-attivita-be/tsconfig.json diff --git a/scouthub-attivita-be/.dockerignore b/scouthub-attivita-be/.dockerignore new file mode 100644 index 0000000..46a03cd --- /dev/null +++ b/scouthub-attivita-be/.dockerignore @@ -0,0 +1,7 @@ +node_modules +dist +.env +*.log +coverage +.git +.idea diff --git a/scouthub-attivita-be/.env.example b/scouthub-attivita-be/.env.example new file mode 100644 index 0000000..9b2e791 --- /dev/null +++ b/scouthub-attivita-be/.env.example @@ -0,0 +1,13 @@ +PORT=8080 +CORS_ORIGIN=http://localhost:4200 +DATABASE_URL=postgresql://postgres:postgres@localhost:5432/scouthub?schema=public + +# Keycloak (realm condiviso con scouthub-home-be/-fe, vedi keycloak/realm-export.json +# alla radice del monorepo): usato solo per verificare i token, nessun client +# id/secret necessario qui. +KEYCLOAK_BASE_URL=http://localhost:8081 +KEYCLOAK_REALM=scouthub + +# Database dedicato ai test automatici (Jest): tenuto separato dal DB di sviluppo +# perche' la suite lo pulisce (TRUNCATE) tra un test e l'altro. +DATABASE_URL_TEST=postgresql://postgres:postgres@localhost:5432/scouthub_test?schema=public diff --git a/scouthub-attivita-be/.gitignore b/scouthub-attivita-be/.gitignore new file mode 100644 index 0000000..7379aa2 --- /dev/null +++ b/scouthub-attivita-be/.gitignore @@ -0,0 +1,9 @@ +node_modules/ +dist/ +.env +*.log +coverage/ + +/attivita-be.iml +/.idea/ +/bash.exe.stackdump diff --git a/scouthub-attivita-be/DDL/1-creazioneDB.sql b/scouthub-attivita-be/DDL/1-creazioneDB.sql new file mode 100644 index 0000000..0ac004f --- /dev/null +++ b/scouthub-attivita-be/DDL/1-creazioneDB.sql @@ -0,0 +1,240 @@ +-- **************************************************** stato_attivita + +create table stato_attivita +( + id varchar(2) not null primary key, + nome varchar(250) not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null +); + +-- **************************************************** tipo_categoria + +create table tipo_categoria +( + id varchar(2) not null primary key, + nome varchar(250) not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null +); + +-- **************************************************** tipo_paragrafo + +create table tipo_paragrafo +( + id varchar(10) not null primary key, + nome varchar(250) not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null +); + +-- **************************************************** attivita + +create table attivita +( + id int not null primary key auto_increment, + nome varchar(250) not null, + autore varchar(250) not null, + padre int null, + stato varchar(2) not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null, + + constraint fk_attivita_attivita foreign key (padre) references attivita (id), + constraint fk_attivita_stato_attivita foreign key (stato) references stato_attivita (id) +); + +create index fk_attivita_attivita_idx on attivita (padre); + +create index fk_attivita_stato_attivita_idx on attivita (stato); + +-- **************************************************** branca + +create table branca +( + id int not null primary key auto_increment, + nome varchar(250) not null, + inizio_eta int null, + fine_eta int null, + colore varchar(10) null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null +); + +-- **************************************************** branca_attivita + +create table branca_attivita +( + attivita_id int not null, + branca_id int not null, + cancellato tinyint not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null, + + primary key (attivita_id, branca_id), + + constraint fk_branca_attivita_attivita foreign key (attivita_id) references attivita (id), + constraint fk_branca_attivita_branca foreign key (branca_id) references branca (id) +); + +create index fk_branca_attivita_attivita_idx on branca_attivita (attivita_id); + +create index fk_branca_attivita_branca_idx on branca_attivita (branca_id); + +-- **************************************************** periodo_anno + +create table periodo_anno +( + id int not null primary key auto_increment, + nome varchar(250) not null, + inizio_mese int null, + fine_mese int null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null +); + +-- **************************************************** periodo_anno_attivita + +create table periodo_anno_attivita +( + attivita_id int not null, + periodo_anno_id int not null, + cancellato tinyint not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null, + + primary key (attivita_id, periodo_anno_id), + + constraint fk_periodo_anno_attivita_attivita foreign key (attivita_id) references attivita (id), + constraint fk_periodo_anno_attivita_periodo_anno foreign key (periodo_anno_id) references periodo_anno (id) +); + +create index fk_periodo_anno_attivita_attivita_idx on periodo_anno_attivita (attivita_id); + +create index fkperiodo_anno_attivita_periodo_anno_idx on periodo_anno_attivita (periodo_anno_id); + +-- **************************************************** paragrafo + +create table paragrafo +( + id int not null primary key auto_increment, + attivita_id int not null, + corpo varchar(500) not null, + autore varchar(250) not null, + tipo varchar(10) not null, + ordine int not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null, + + constraint fk_paragrafo_tipo_paragrafo foreign key (tipo) references tipo_paragrafo (id), + constraint fk_paragrafo_attivita foreign key (attivita_id) references attivita (id) +); + +create index fk_paragrafo_tipo_paragrafo_idx on paragrafo (tipo); + +-- **************************************************** categoria + +create table categoria +( + id int not null primary key auto_increment, + nome varchar(250) not null, + padre int null, + tipo varchar(2) not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null, + + constraint fk_categoria_categoria foreign key (padre) references categoria (id), + constraint fk_categoria_tipo_categoria foreign key (tipo) references tipo_categoria (id) +); + +create index fk_categoria_categoria_idx on categoria (padre); + +create index fk_categoria_tipo_categoria_idx on categoria (tipo); + +-- **************************************************** categoria_attivita + +create table categoria_attivita +( + attivita_id int not null, + categoria_id int not null, + cancellato tinyint not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null, + + primary key (attivita_id, categoria_id), + + constraint fk_categoria_attivita_attivita foreign key (attivita_id) references attivita (id), + constraint fk_categoria_attivita_categoria foreign key (categoria_id) references categoria (id) +); + +create index fk_categoria_attivita_attivita_idx on categoria_attivita (attivita_id); + +create index fk_categoria_attivita_categoria_idx on categoria_attivita (categoria_id); + +-- **************************************************** materiale + +create table materiale +( + id int not null primary key auto_increment, + nome varchar(250) not null, + proprieta json, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null +); + +-- **************************************************** materiale_attivita + +create table materiale_attivita +( + attivita_id int not null, + materiale_id int not null, + proprieta json null, + cancellato tinyint not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null, + + primary key (attivita_id, materiale_id), + + constraint fk_materiale_attivita_attivita foreign key (attivita_id) references attivita (id), + constraint fk_materiale_attivita_materiale foreign key (materiale_id) references materiale (id) +); + +create index fk_materiale_attivita_attivita_idx on materiale_attivita (attivita_id); + +create index fk_materiale_attivita_materiale_idx on materiale_attivita (materiale_id); + +-- **************************************************** categoria_materiale + +create table categoria_materiale +( + materiale_id int not null, + categoria_id int not null, + cancellato tinyint not null, + data_creazione datetime not null, + data_modifica datetime not null, + utente_modifica varchar(50) not null, + + primary key (materiale_id, categoria_id), + + constraint fk_categoria_materiale_categoria foreign key (categoria_id) references categoria (id), + constraint fk_categoria_materiale_materiale foreign key (materiale_id) references materiale (id) +); + +create index fk_categoria_materiale_categoria_idx on categoria_materiale (categoria_id); + +create index fk_categoria_materiale_materiale_idx on categoria_materiale (materiale_id); + + diff --git a/scouthub-attivita-be/DDL/2-insertStatoAttivita.sql b/scouthub-attivita-be/DDL/2-insertStatoAttivita.sql new file mode 100644 index 0000000..e530a04 --- /dev/null +++ b/scouthub-attivita-be/DDL/2-insertStatoAttivita.sql @@ -0,0 +1,3 @@ +INSERT INTO scouthub.stato_attivita (id, nome, data_creazione, data_modifica, utente_modifica) VALUES ('PU', 'Pubblicato', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.stato_attivita (id, nome, data_creazione, data_modifica, utente_modifica) VALUES ('BO', 'Bozza', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.stato_attivita (id, nome, data_creazione, data_modifica, utente_modifica) VALUES ('PR', 'Privato', sysdate(), sysdate(), 'MANUALE'); \ No newline at end of file diff --git a/scouthub-attivita-be/DDL/3-insertTipoCategoria.sql b/scouthub-attivita-be/DDL/3-insertTipoCategoria.sql new file mode 100644 index 0000000..1493e95 --- /dev/null +++ b/scouthub-attivita-be/DDL/3-insertTipoCategoria.sql @@ -0,0 +1,2 @@ +INSERT INTO scouthub.tipo_categoria (id, nome, data_creazione, data_modifica, utente_modifica) VALUES ('A', 'Attività', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.tipo_categoria (id, nome, data_creazione, data_modifica, utente_modifica) VALUES ('M', 'Materiale', sysdate(), sysdate(), 'MANUALE'); \ No newline at end of file diff --git a/scouthub-attivita-be/DDL/4-insertTipoParagrafo.sql b/scouthub-attivita-be/DDL/4-insertTipoParagrafo.sql new file mode 100644 index 0000000..049dd4a --- /dev/null +++ b/scouthub-attivita-be/DDL/4-insertTipoParagrafo.sql @@ -0,0 +1,2 @@ +INSERT INTO scouthub.tipo_paragrafo (id, nome, data_creazione, data_modifica, utente_modifica) VALUES ('TITOLO', 'Titolo attività', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.tipo_paragrafo (id, nome, data_creazione, data_modifica, utente_modifica) VALUES ('PARAGRAFO', 'Paragrafo attività', sysdate(), sysdate(), 'MANUALE'); \ No newline at end of file diff --git a/scouthub-attivita-be/DDL/5-insertBranca.sql b/scouthub-attivita-be/DDL/5-insertBranca.sql new file mode 100644 index 0000000..435ca30 --- /dev/null +++ b/scouthub-attivita-be/DDL/5-insertBranca.sql @@ -0,0 +1,4 @@ +INSERT INTO scouthub.branca (id, nome, inizio_eta, fine_eta, colore, data_creazione, data_modifica, utente_modifica) VALUES (1, 'L/C', 8, 10, '#FDD835', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.branca (id, nome, inizio_eta, fine_eta, colore, data_creazione, data_modifica, utente_modifica) VALUES (2, 'E/G', 11, 16, '#43A047', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.branca (id, nome, inizio_eta, fine_eta, colore, data_creazione, data_modifica, utente_modifica) VALUES (3, 'R/S', 17, 21, '#E53935', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.branca (id, nome, inizio_eta, fine_eta, colore, data_creazione, data_modifica, utente_modifica) VALUES (4, 'Co.Ca.', 22, 99, '#8E24AA', sysdate(), sysdate(), 'MANUALE'); \ No newline at end of file diff --git a/scouthub-attivita-be/DDL/6-insertPeriodoAnno.sql b/scouthub-attivita-be/DDL/6-insertPeriodoAnno.sql new file mode 100644 index 0000000..9c616c3 --- /dev/null +++ b/scouthub-attivita-be/DDL/6-insertPeriodoAnno.sql @@ -0,0 +1,3 @@ +INSERT INTO scouthub.periodo_anno (id, nome, inizio_mese, fine_mese, data_creazione, data_modifica, utente_modifica) VALUES (1, 'promessa', 1, 3, sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.periodo_anno (id, nome, inizio_mese, fine_mese, data_creazione, data_modifica, utente_modifica) VALUES (2, 'campo estivo', 6, 9, sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.periodo_anno (id, nome, inizio_mese, fine_mese, data_creazione, data_modifica, utente_modifica) VALUES (3, 'campo invernale', 12, 1, sysdate(), sysdate(), 'MANUALE'); \ No newline at end of file diff --git a/scouthub-attivita-be/DDL/7-insertCategoria.sql b/scouthub-attivita-be/DDL/7-insertCategoria.sql new file mode 100644 index 0000000..e5b2b34 --- /dev/null +++ b/scouthub-attivita-be/DDL/7-insertCategoria.sql @@ -0,0 +1,9 @@ +INSERT INTO scouthub.categoria (id, nome, padre, tipo, data_creazione, data_modifica, utente_modifica) VALUES (1, 'attività', null, 'A', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.categoria (id, nome, padre, tipo, data_creazione, data_modifica, utente_modifica) VALUES (2, 'gioco', null, 'A', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.categoria (id, nome, padre, tipo, data_creazione, data_modifica, utente_modifica) VALUES (3, 'danza', null, 'A', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.categoria (id, nome, padre, tipo, data_creazione, data_modifica, utente_modifica) VALUES (4, 'gioco d''acqua', 2, 'A', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.categoria (id, nome, padre, tipo, data_creazione, data_modifica, utente_modifica) VALUES (5, 'gioco notturno', 2, 'A', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.categoria (id, nome, padre, tipo, data_creazione, data_modifica, utente_modifica) VALUES (6, 'grande gioco', 2, 'A', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.categoria (id, nome, padre, tipo, data_creazione, data_modifica, utente_modifica) VALUES (7, 'torneo', 2, 'A', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.categoria (id, nome, padre, tipo, data_creazione, data_modifica, utente_modifica) VALUES (8, 'olimpiadi', 2, 'A', sysdate(), sysdate(), 'MANUALE'); +INSERT INTO scouthub.categoria (id, nome, padre, tipo, data_creazione, data_modifica, utente_modifica) VALUES (9, 'gioco giungla', 2, 'A', sysdate(), sysdate(), 'MANUALE'); \ No newline at end of file diff --git a/scouthub-attivita-be/DDL/8-insertMateriale.sql b/scouthub-attivita-be/DDL/8-insertMateriale.sql new file mode 100644 index 0000000..e69de29 diff --git a/scouthub-attivita-be/DDL/insertScouthub.xlsx b/scouthub-attivita-be/DDL/insertScouthub.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8f356d2dea7a971667544d21d967e450fcd7d562 GIT binary patch literal 17518 zcmeHvWm}wUvNi7R?(XjH?(Xj11V{qG-CaX)cXxLuxLa^{4ZI|C=G`-yJ-^_b{?O0G z)lV(x?y7ZHt*Syv78DE(2m%NS2ndK6C^TgNIuIBLNa+1XR3Io2T~S8|S91qfL$!}i z<^X*L4|_YJ954{7EFh5g`~UCpzxWdvOWaWCVL}@?m-h_LZwx|Ln#O{_EJ~Ki5fHMU z)Dm6buow3DJUQ|WiwHneBAaURGF*@~lbc4}o zcL)rnjw_M;r*wjCb{+lg*JuqQ<3Zx60b7BIA;OwrU$8mf*eo&$Fp9I;J~D7a^_ zl+?%@45xEnaES`=!}qC5V>8`BRrw|b(2W<;xXC1q1Nl(w72wa>PYK>I8^mZ_`FV)& z`DGUSnE4Vf1EDIsuB63O8A>mv3bDS$X_41NC)3NM=t|3{=(2$MO>*Pi@!sA*ft3C& zdh6AhNN?U>k$VqL*!So)bTPLBFf#ml{=caGUmU9c^481Z^65hcb6(D^O4G}J({MpsVG}^c!N}8=2EO0X_7$`2O2dW zF9?G_)mLXgUVGi-p#peLMD?^hsH%}O>nMIQ-FqRa;1FIggj??9dn)=6z{GN?!gI)u z`1TP;UDb-)y2>Qeg@@#$p_yIRwOD#5>Q7I4g^VE;5;l|vmPyG$@~m5LojSJ5(XSu- zIH7w>hc1Tw!U>Dk-w(3?3=+FS#Ri7=SE3<+fN+4IfIaLO{}CtdjxM$)j*hm!4!M8E z4DkC&_kQpH?nhgqyiG3?Qs8~ydbgF~s2fTleQd22wb@DmB+}I)OEr0}vbX0mOfnBU zn7L_P2IbA(4)fT_-G)A{mtVI8t;BaOVq~i9QHi;THbTR*GwFb&(zr$)U{X+`CCo<7 zR!yts58sqH_$4O9iAldNzs9Pdq3}dip9wc7%cMu?YNn;#>1BApv&qI~q`eX3rF@hK zjzb1e>A&qvwN5Owe^6%!$*g}k5L$wzFZ!Uj3D;cQ&BBt@X@bM6B*MsAmSpzueJ=o| zE0dc7`tYIVs@o2<*UpPc+bCrWG9Du6azc&(i{hT5){4!qaa;OI;wz;`oF`g(SFBxI zY?FNBs)_J0e1hYAQ+w#XmPJjp1wL0=J>s76wAsm2OFHwZ9^P^1ovWKf7~#8<{HM1S zByMBvfdT<_LjwUJzTffgZU5?LW$L<)xjaZ;1iwEbhPc~|v1b*OBKb8)o|f3uo|^9Z z#CyP|uv5sZpL%apqpEzGR#v!ID~@Djy&fOq+?ioqDPiMKtBY7LMg6u|AbgyGS!NJ9 zettcMlPW6j{AG}%=RqB+;oQ_Ig8~t{#h4}-QjO$+tYkBb)ITr0w1X3& zeOhBQSLqg&3E!diPfb{0u{)*dLJ`u`#YLfcss^%~TkDmAyBYKR`T(OQXL)ZoIVCE( zP0R-v(qYpYtI7~P+^@5r+gZJc*VXz?#1Z^f(rAwrMPc6nu1P=uhw46z<_p4&cF^$!7mAQ_@MW9WK61y ztUmK#GIw}qZYvwDSetP#e$$Rm#nhRey`;-%RwZmT-ljNXbdmsbo{*+o_)J4JXFB8G z*2&vKw(<(L;yf6Ji8XomI;nD=!?na!1h&Kp`UsL8av`r~$)o_HinRv+1JTBuuTf$X7?d?H50ubHAJSJTS(xxFBY~R@nfLPKfY&Ilcoh4U-eB6NV%F}}QEGh`MU3so+F{?&? z?oy_|Kv`SMm_foT-3qiv&83{WBc_k84Pe>>fV_dq=4KXY;W{GI59tWtafkbNlpqzt5!DF>1SI{=2Uvem0>H}L+!et1 z`@;6?3>r&ba@gTO3p$6r!G{_Lk_K9-;c%?yud448PNLp6`-DBkf{#iXN+WJb`*g-X zYoaTM)O08gx=7S-_<}fm9hwugk+}`UkCdsL6v37|H;*JO(vUa&Q>wKcI*lLR9E4I9 zuR0*2&N9w=W1#bu#nbG?vVlU3$VsXuwgH0#|KJlwg>*zMhO9KmNZ2J7Y}1eXPf0F* z$sY^3zK^@L<_@M%=xi}<276S^g$4DQZc$K#Qps_JR{307+NN0~>f#ZYV4A)H;yFYy z3XQ6>kkKcbi-WeEoZP-KMPVY3Jr7qohh%cS$UW$)r?mZ4&r1o)Ft;Pu*(}2}{lfpV zV|~H7>BA@fH-9@g-TfcFBVcD`W6|)#P7MZo6Jt+V+LpEy$>E3`uH4^WT?+YIP2~&Z zYj-}4OAns0efH{ggm5$Z(9&Seoq6BLLBK$5rZoZMT!UkcYU*@o_2iLqeeEANXjd5p zW-ul#CL&A}l%0(^u>CQ_{0I&- zjc}gynUxS)%^2&+4e$BZMou{D=Q$p|lY}0|PvK+}cwu8Q*_xhoDZSc3?Cg76;BtH| zg5d?D$CFq(uqtD!ay~hN{$7yi;0%5eBQT2Od@TXzfvyw!_s@yE&D`@76??C@@I0+8k5M%-_I;mft*j#fx!KvK<;=b{zI6k2bZa#? zBi|WEv*;8@T_AR$pO~AQZud65DT1rNs;!UxaF3D36EHCQTJJw4au4#S$=E&^+(oUqA}QKH5G z+hx@@RY=mg9N=k$*heTs9^as?U$Y!dR=)&K^s8(Ge<=Cu?+}1Bq(Is|E@XlPRbu5q zVOmM6J3Si&u6=_yazjXoTcOrE0XnAfLDz@NU!qNu59IaU7*UpjvT;`HT1T~HR|YRb zGX&KT0w0Wddz>>7&aAv`miyf3E7v^gd5K^8W_XAGIEMFP;&N3-d&~hIuD_h98KdjU zoY+1@oAx|nJ-E^6f~MxDgU5b)9$K4LRM3tl=(ub@R6DA};2^vw?{W9+mHL?JxEwzW zN${}Eq>l$s^;BeF?ktoJBB>dP?$KXV39of&1^>{k@=@rlhlf4|iLRnz{=!Rob9Kd` zd%n#?P7U)SPili3IUOyZi`-MeaC0TAl6ZhRBmD|-C^m_OfvZ47)Yi(|m`KbZ?R@i! zR=Z2Mv?XZ+XeD~{Qt)r{prcA55%zb|^M?Dcq{sf7^fYxHx4F>17+Syjn@7%Szy#=h z@&%WO0n(j4EGsxYF*QelETeFq=eVg^e(M;NmXZ#mDPZJPDK&kr6(rpE6cD)S^@EEl;`MarhJerRck^Ty z(@23uhJw89hMBvJCV>S{k&E$_)oOIb!%<<-SsZaw_ESNBN*+ystqIv$RZNlvxgpz} z4Dz;Yl^1xmC=3AilO-tTU`h|1bd26$Z>4~yDQMgm3>k2d7v_|Myht2*O_80q?lq=g?u2qclF%LZ2dA?&sVtXuHVYuQ8idzZMi}^nX0GfG2?w%MaU9T zC4i2?hO-$v*XmNueX{@b*nN<@YE4Q%2)>Vq(-dIO(?w)02TBho)@4^Dx#Qup@5J)x zKEd7i`8s2!Oe*;2!kwGHb;Xt-xDJvuKfznSNWI@Eh&y>6vi9H+ulI|8&8|%M0d|IZ zcs{1Uvl>K}%0+>_X2r8utg+#RBp7gGS8~L`vFR<{&*A%WygN{Scd&S0*`8qz(Naf; zu7#|=fz}lL!M5cVh=T>8!VZ@jSdOYghQ0ZPakuIj9*#=;MXmvLXd2+)h@g8#=Y)oK z$A$HSJ1j$k4Y`huoYCVIM=1!;a*bngTwK6t*8vtTX%$4zKvSK8_0R7LGQ9Msa3}9{ z*3y!`+VvFQfqjVB>Yf|{Ga&j!@q0q9DFzPJE;Kum>=NwGJ#^w!sFuvA1r&1!BGC$P z8c4!D>QG5cTZr#Gj2bXt)K`X#UmYwbZpPv_ydxKN zHa#hXI8;!a2wS3jf7F86PrXyv^}+GMlY=We@WwqY$kpLin3Ib*+u2VnDjM;ouR$bX zeIZsnbWb<%*WD$eu#E3vSF?q6az*CNiJ5U``)zaZ^xT-+k0u&VgNX_$~5ad zhe|CI_Kjqhrpu}mmUiUv-|tRb)z48}6$-p0_>H@}H<8hQM5U-j6-D(cEYW-n;uj8V zv_u(YMKd$$PLyi1Vln2(tF#?F23h3<*X1bCG*%LP;GvkC*WaQ3=(=w9=%sJpM6*v5{TrC>-;V;5rj@GAJ=9(>CyN; ztq3Tk%_#B(qqtfGcv|m>E#-lh|H07_5s9#WDvRx#gZ0d zD*5OYcg@RJ(II}a2-&KtP) zC)uA7yeVFEKe8TP>&Oz{a>*{Luo2gEoC3TaK)td|D?`)>--L}L8YYJ66M@{b#~hF2 zrm0O*TaD&9A&xP+f9!Af!ru-q8+r}0L`>9u;5pDOpNX2|P_PFu^VVm4sdSv&zpI;j z)=iJ_gbCO!LZTe4w!rO$&CNfISGLJgOjsq)wrQ{F!_z4zK)sXc@T`?TU31EVMnv63A=Hq2H3i$?wG!rx?jWiRZIqGv5=H>cxt2G5|9tPG*#U8Pt ze!WD=@ss!6EH*snjQGz_TD+}oY2Hl|mdOj}%G=eET8<8BIywwE9$vQfzJH`=&3?crv;Xjenh& zEuKy^g>z{Bv`y#joT)K~6V4Yvn;38Cjiw3O5)4kqtM>AY2q*4_N3i6S%15?|;Nq#$ z%=gLC@Jk)z-qd~}KXB>6R%VroBGqCclF2@F;7J+XJfAX#ZRR3n3%c^H7=;6W?45li z_htQQj<-YpF{_MKOO~dFe-Dd>19nr9_Q0~XgnGs^Hkkd$s!-Ln;eHMp7`ATI;q0v^sB2+G%jBrXZc9913XpHlPxioUgp$z@ ztwP6qkUErWq^Ea`P8d>+47&h%Vx8{F+^!F1rz@c?%n7BlMM?cQb6z>R$m%7j4{1dW z*To?(xuhpvYf^14UpkC_1_8FVj@>nD%IQPg+^t4c3+r05bG)x)q&xes*^S7Wx^w(VI>OyM@;fEu}9#S}>@j`N>9`N<#ic|?&Hs?GnmP4K&o z|GQ1hZyVxw8^2Z1A-~HI&tPso4}|q@f;KO3MzHlQbumeZk{x=@;w}1c_O@rSMv@W_ z)=T!SQ&0npDW-zOLPo>J10qdH9uTS*xtiiD*ke;RVID?+T05di9iT zGww?q)YYZ;k8DNwsoZwa*ThOU$@i=6{T$PS!Y{+xdOgK+!NAQ@3yzE$w^#HGN~zr$ zFh;=)5TlAYok)y~zcdE9a`?9Cl|l=eCP#$6Ks4B3--m_B!)fQFT+lv2X`7{p#EANd zDWz2s*fQ_JJ#0+6iF-DW+-izZk<+tWO=^5;9(m-!n3dQNTji`eU!+u z2Vk8lzJdzgZ}u-^5Aj;6X~afSePETfboj+37xQcsIpZp6KqyQE@o+(&`C$pF+xy|$ zQgggtJ8&rdNP}qN@Q}8vx zmN33ci#I|0OzNJzL>`!nZ5Xs`j-i|Ye!E7Rk3yp;2SGlf=6y{|UCivazbPP;~^ zCiB;MZ{)~%b?&twl`j6o*N%ibgZ}*U;RyG*TUwUKmIo`;C)Ah9nFaZklVkcBCP@17 zh12?=C#S_0g1hI1hP89|vTj5^{AXe+x>Au02vj?p+;j$w!kza_M)Zje;pu?a^zy4@ zo?03LMa2NDbytxmjeM)wi8O;daVkO|GFwh&9U{q;U9mW6ii7aTwk`p73zA0pHo=`c zH&^rc+n$v)SiRPb;(?_t6in&^8&Nvdb5G`5&X&#B?iqnLgu}wvHyhP0-&o?r1_O1= z_z1bd41Us!0Eo~$u`?$7yzb4Dk;BD!fP9y)tdRCfF^5KDOsS?xt*Bh3sfPVp6~c0G zmh#|UuvTLfcd%AvRCB$LKaYp1A9gU0-{Fod8kzm^bq~uq?xOzcc;_mqyYm}1bhLfV zt;NznSSd9YdzFbuDN)(j$UKd1tKGi&vCxW`;rL4MBYv4})SOU9ZS9GZk1~N@qY~*2 zdO}qS4b?8)F7bGXpJ&YRw14d>Zxd9OCtfc$j3jzjIviL*bSva@k@_5sl>Q++?zv0* zZLJKb_^zM9zSOwr6j#zU{Zz59q)98ehe9nPCm~z^Y(A4_7MyEEo>;|<)44_C-gbfU zp*%QcI#rL1zI}@0@M9A+>JJj$!tN1I$$hD&$NfrWCI2wK0WT2n;Tv=^1Mne5+;N@z zqxfbICdd7lgOAg@)__Y2&MSN4BkwMRN&U}t>6Tp$3vUNlRfyfFKQ}_(z;15jTPjNd3Uk361cxmT#!EqXlnuxuqbO|pvLc&Ebdx?bZAHdUs95ueuTp zCNNMhFb1Jyy}{;i?OUQWo(X#>>&g`2%)0q`4d#loPr_7*i2HX6--&9${cG;WD8&;` zuL+g991)gG#OgUaW3lTPNjgKg%bzE0&OxziWYJP10G>WZwe8BYNkMK!J-(3ul|Ucj zWYG*NbvQZIAn5TByDPsvpFTY^v2GJG1Z~xrK1=m ze%Obl3rELB>ivz=glZ+HVbeJ<3jqH0^Ubrx2AOgHjli3~Raq4EL6|l0M$ri&>@ZJx z(+OwI9b()ET9%(JeMZAM#IL#DZp-KHlI;?Xd^qf$l{3~6Cs7Mk6U+N+9roaNO9zT4 zJjkGdA2g(!W%!XDT3KyM|1_@5Qd5Od0OhAozj!^?_ z^N|U*7^5kTXpu@tfwg9KbSOy$=>sZAH+@Jx(bIz#==l9sd~dkd_C zrPU|)Rtc6LvWay2@^*4`pt+`Y9!Bs1k<$ZDxV-C&K<|a*nf^MYd(~?nv*}L`0WNrQA zc(g>d^sKG!@5NTZS3bi$vpX3MOm<~KoQ0Mt`Xd~WR(I%m`Yw2Zl`I;;fvsDe8uvm< z!m=~a6ZuLge0i)S=T4KiTu5HvX~9OKNg6^p`mxemwB&c@}QaR2HQQk6m82h#Pb~zL79Dv9J{x*56??3 zzO?~DMpU0cPm6#QBcwBvbgcaGq#(bBw@E4w2ZiX;JS;n%mlgjo5o~$}v@Zv94QJmA zgn1AI_xBJp^o6RQK>Ls?3JVNI&=~n6+|6|O-oQ}U`yB4PBDXWU67~-t03D+49qOdR zP&}_j=h7;Im*OL_d)D5T&cMG^)C01uzN#5VCqU2v&zRvIvNKZSkId3W57}3dyL> zcFV1`Pe3<@Sysf(*DH9QM7USlN`hdq<$@tsp^Y*Yze^)U(KtF+k`nCf@oako(zf+_ zkEoH^Gp?frTp7TP_L=JP@?`YFigoHd7|klWd2+}4fjP7%s|g|3h$X+)h@)M>k0f+^ z&b6jZHq5M#|Klz%avT+o0iL3OHWx$c=26j{a&|SQnBS>k7?C9T(BlTt+q=8|XDfK` zlzRf>-Jdz%{Tc0l{MpLb#oSEQ)y3Mu^0!;Ns;xLI3n6(`&U#m61lJ+YD)v7r%r|vR$?k#Ntj3o)qe&D&DE{w+xH5r*!H*~=*Y#;|Yt`NYE3@e?Gt>=aj^g8jra0-HU|mYOug;tpzQ zAaXu3FHGq&jxQfE(E|-<*ylCi5(Yh5P%^J*B<(29dL3Al*pGh9?C-?qAv7^8GX&gjk(M&QRZr~CAZ1dsqo$Xoo}8SVoY%`Y)cA1Uj)x)_lI@D)vAN(8=II~RjL=ASMTf4w@XX!za3yfK!z^VcrTZRh##&v7q~o^P>TLw+ z%94EmP4-nEED8#dL5#-^Fma3!$YoD-@od;=^yVV#@FH%o!6J!JYpe$LKJVb*KAyJ# z!1J~|7+SPYOlo9CL8)B@8cCYcQ)cEPWfLbsq7HBzOrh$ShYU}5*wALVtz(BX`gw5* z8JMpp&Ak(4NTSY3;;0Kqua?0UY+R2cD+5Z|rt;XRs_&$bW6wyvOun)LlRRj`HrQ;G zFoZmH3xE1FG;~Gz93u8uEKM;ag&dysE?IJQ)l7Ve#8Bc1tFHp{F&NWK!nep_QFNl9 zd_O`S;r)@er|Is2WUabwfP z#RFC#zSJ%mR$aZX<2r(4)fiOIN7MXP2uJFWj)#?wORJGLgWb4F1%j(R(Gp%4IsTkk zaD?_-xdyGAPPe#9L5_A?>4|4Vc05n;R2FRfOCp*%678J^rw}YVb{nvcl3PXEqluVQ zLqYOxhtw8)>a8yH-H1(Cz(Szd8f@Y=^+c?EzIt>4X|{vx}wVjWR!jkYLrdDI-syuH-+Ca`Chk%cL=|EUUbI~ZtMYY z)uUDg6!sfd&kem^1~JcbH#**~?iB^b3k0H*IbN4I2?d@XlRH%qX&Lk`Z)(poe_pK3 zObR~T9xL)Kw}KF^TrkUrh&2vjw~V4{Wet9Dp)*i0!81LQ+Qf4GtcIw50_0fEbiMVd z@57cCFx?KQfuVqt9e-HAit^<9JW3xhfy=}vJr8^*)k*V@9&K{_ufzS{2iE!@W;xvn z3l)5VN5Q9PZ;MX``ms|ahE;_IvcQNR`}A350C=M_`oXhytmm=YTyzUp_OjMWZCoSL@) z88YF5ilu@fP@1m^{3L*m-pt-P?rrTYwr-Q;Jzb;^lpnSdu2l~0Ik>BR8>!HIVi*ky zSCg`OWzM!3%H2&)GW4IcpRPZUXFRkI5%_yOZkped55*)W0h@RmZ$vNyD5y{@BC@+K zrs+etwiv~3nr-@$44PA0t;3mwjDO7XLd$>CeaWExoa0?^a*4@=sdVvx{`OVH5;Ong z8?BSu)_cos9Efp-7fd0}exjw!hM_cV)7xBHlk8*3K+u^9a9WI~R*F zi(QRA?A%LKGKIzM^Tj;S2KkNj27&^ClN+Cg(51 ziY+tu$Z#$|(ZZX0AWziJI5ZoYm92)JVC}1*nv5(lz5rz)`?>z zUX{GOfy1xOZwe>w&#_SFb&?_1!&75*RG?lBun#F4>I6WlDzx6ICk$uX;nj*qT%-0= zT*Em?xtd0OTz=^KG_nAPu;#XPsYh7l{XFaSs$9-*8EfHe!Bdu_C@+}W(IL8CQ^F;N zU&?aCaXwqK*>ab5Or+di*auKp!{kDun+Y4no2lzxVJqY2CHx@Y+U3`EC1x7D!C@J& zfLi(K%768-Y!0%orGhQgkzxE)*Z#{3(2yz6eXwjn9yIx?HY}XDp&MxGG=yDjFij=$ z7OoD=GW2?>*jklg_*1?NElyI;CoSsw9EbMV$ed3fu+(Y&4Nj5q=x7v5l--O4{b=DD zMk-IoALi%0YC7_pS+=B8(`g@Tm{;}IG;Js5yxicU2c9ehgwUk++a>I0D;hsXsc0`cy|1xV6O#pr{>(lGdPRJ2du9CZap4=lLp1xs|oMv1;51+m^ z&vYU%GK!`3QG&3Uv8#Qc!q)peY5UJ~j&^9aY5rY#;QsI0GbM?2_TbNS6k zo3uU=gxI$M6gEOiO1w0+;b`#0+#DrXOMW}T-fGn5?U{fQDNaef2(uQ&h_~{iSH_^g z-NlM6N-jEj+@)(kxf>*D+7qAAp-BKCA%l(~b6i*+aOg)F$%jap7yFle!d^^N$VvZyf%#=yvhUkk}NP8IgYNp}6lOjGVl z=ST%UpQPStM{6mkXQb)+9p@oa`U$VpKPN#Mj$s%2qCJ0WPdD+>+Xhe;8-sU0p zm^x#U@C8Ty_h5CH>AF*v8){`UKC~BbU?*6U;gEHKd7^8D7JNAa4 zJs6W)&NS7zla@V=dI$Yyl+_{dDzu1^w1#D;c4@t%pN)gqeoVfKvs6sLS2k zcK3T9hDt(*n43ox@zIaCxU9ds*r-(S0q~ZpE$J>T)co&Vq{-oX$#a6IZ*b#EossVP z#P-y$VrW^GVOgdaSif4>NCIFT6imNAkv5~zk~;1wx>F;D%{G9Lg)vXpilhx zV@N#bSa-Ncr8#SPTIj9v5rm-z@^(V=t-_Eu1Z9kxhMOMkV!U1^VF>DdJ=&0^=FfBkRcTeQq~_$M-Sib{Ah&~?wCZ5o87|81`JO- z*Z~z%a40paJ4f$G=4hg(<8hjFGe^^t_(3!cW!6?qxJuRVvCcRAh;nJn)0_2SQF9sj zSWIoZ>z#G7YBhpjSUJ28e2nRIArUyi+cj>di;7FOTG9mhKssF-D&K|cOy~rRt}w8X z1R3pcekD)2cm&|B;4%%5XCj4t@(%%*B@eSs`IfX_Wf#6vHxtydohl$YsmPoL$oHtR z7kB!W=fvi=T=gP4G1ma>DEhpvT0f>F!9sJkgj3<{713lAvwjV`17)d4C7jYj<+pE( zAi_6(rJv*252G^D0H0*>@eu#*Z~GLN1LVRf?_3c6-&IJKf2fcv4l7(pUW`?5LP+3o zKsDyW?wT6JD=ZmV(wOq!z2K}uYeJ-QWWG=2A9UUrKAG|+*e|*Gbq)107?}p~%x=L= zI*ILHo2F&`q652-@=u_oY2bWjOC}+Xjs^7u5-PEDWYCibNP^0tg00?OP6SO&$;`jz zTPT9LG-wD@3gArAP_*~C$ALndq(c3&wNh9x69W2Wtxc9AB?jf7M=#QhiK4bPmHR+= zwz^sJQ+48@(vZL}M^kp+=UX5Kr}D*C8LxG^X7H%?=!}nwiTbJFA@;)~U|AH2&LvC5 zL2jrvEqgng^$R>+P4W|d&$sX}r5~pl7Hn`nQ$WiUBtH_0o7T$-Ys$ek<<@Lq#?3J_ zpWtg>e>M9WC+C=qMW@ZxCzw9t(7JXcXB+utq2h5zqjv8@p@F?gz0S|fh2syccZ?@I z#Q)GBt$u5eeFev8zcfh5=KbFq_S7A7R%+8om~GEqMNu^-WX01HG5 z@IvjlmWhcC1}1u&2S?vMvG2@^ug2M7TUA}!(AHu}p-%&Z776qw;EKF4r3^{J4{X^i zJ~V+=M{d|p0aV*Oeu6|utA1K8InmMVQ=XL_hdjQS$K7U|aWP9H^>_gAY6x7ry67c! zyS8Zd(nT{uTXkk{LR-!1C4u>36HHt==nyuSbAouP(&$cu$o6zq&o3WNy<3I%H8~Aq z;T}}_M?d2KCId8F0xeKu)4J-N$gg%~h<9*a_Z_QG3rufg%%ttOl{GX;*R4KOEt`JG zt|KC#=6v}OFv5PVKxxygXoGGUA~XK$B5-wTL^$m$0*mFEd)9uQ`9Uq2QS(R?$vDf9 z={zMqYoO@&-Fw*)j=o>O`H-}~LEul>ns~K6K&^Oz{C^eF6fO$GMny}=Gcv>L>0O1q zbT!KKyB?qYw!svmh;ox(Xo-IR<9ln%ZN~%B_A!k}IFEsNN}K9s){7*1m3!8xDyOIY^nO}&|B7azQ!ernScCyTeYa^Z25RSEgrm`%%qzo z6a@$UXJdG*7UzKkzGpvREwR-;Vo#4c(?ODXRf&piMFPKpZ5xk72XS*W_hV(T&K?yd zH~PW}HUorLL{_^{FI5GRXa*n~%orvYD^m~v6em4#gDJgHi{_egibXYkg|qwmes=%o z$*#f3);9KDdjFc+M|$57F>^Fka&dG5Fd92K{fgrEU8?`v3wakFGUB@(R(|agf!-km zoky-YSOp8YG3U{K(y9iM{!Fix+7LLLLsTtMQ%H!PTTyGldm<4#Td6q9_@VvS?z?G7 zkX~#i;}x55VKGYu_4nY@)1_p#P`C;ZS%h%cXy$eX!7ob3LIj|=5us8|T6y%fh8Ehh z^lnI)DsC#p)UI7MaPkr#?Ag|1z8p?DyT$G&W1nH^2hOE_1DjEHUb=n@)5wGDcxYPt z49qa6d0sx<sAqtWL&oKR2PT0-jR$am*NqT7Vh0oO^6qX9H=@1f4IRmNK5Cey|!Do&W)gX z%1tyIPBfG0o*dIWfSKlNaEFolfhg(07B_G&mnCn(!<3oIRLRf|O#{>Epe18sJ5nt` zWJWl2aY1ujkeO3Ey3_$GvaxhK{L_|(coQ&X?QQm6rtO@2A9z+KN#6O9}88=Wz@R{54iITe60Z|?3-?HbYW|- z$E~*}?A?#PJU-fLuEddTtt&+aI(q1SUX(ANDmN`R43PyO_9092Z$F6UFgE*Z`}{o}+#g;kEwF zR*pcyBP4TbC3N;7yZmr~2M8Lrz%xVt`>5XExS7>uJE;_krSR9K&VSL>^i?1o=T{{FMTWbjd{@;&CRXcSK{tdAw=G^HZFa zcmiCoS~Mp<7>r09=&wkdzdkxVOTg_}dhfIx=To^-?@rCG0`R_Z4glp4S$aWzW2mAD z0IgbP0~Xk}6EsE@;Szq=HA1o3cEC(<$ui5U_*u$?2&idZxXHz5z`54tF}oVOhaoHq z3l2?w^g~p9M6`u=D@^qu4L#lTfUzhY?^kULEJVRr=Oe86VO41cTt(}Ps7ISf2u#gj zbu?Z;x`-+?1D_R(rYgnk7wH#eCCBVv3U8iL-z=ur@2h|s?b`(oi3wcP1S;UoGW}Pl z4H$&}y$kX8d;kCR?tfnY=Dz?a$^JXQzpozu)9}}|adOvxD|0!k^uezYy}Me)@#F3n^#J`pN2w&G literal 0 HcmV?d00001 diff --git a/scouthub-attivita-be/Dockerfile b/scouthub-attivita-be/Dockerfile new file mode 100644 index 0000000..18b5190 --- /dev/null +++ b/scouthub-attivita-be/Dockerfile @@ -0,0 +1,16 @@ +FROM node:18-bookworm-slim + +RUN apt-get update && apt-get install -y openssl && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . +RUN npx prisma generate +RUN npm run build + +EXPOSE 8080 + +CMD ["sh", "-c", "npx prisma migrate deploy && npx prisma db seed && node dist/server.js"] diff --git a/scouthub-attivita-be/README.md b/scouthub-attivita-be/README.md new file mode 100644 index 0000000..8c9a388 --- /dev/null +++ b/scouthub-attivita-be/README.md @@ -0,0 +1,53 @@ +# scouthub-attivita-be + +Backend Node.js/TypeScript/Express/Prisma per la gestione di attivita scout — +sostituisce il precedente backend Java/Spring Boot. + +## Prerequisiti + +- Node.js 18+ +- Un'istanza PostgreSQL raggiungibile (locale o remota) + +## Variabili d'ambiente + +Copiare `.env.example` in `.env` e valorizzare le variabili richieste +(`PORT`, `CORS_ORIGIN`, `DATABASE_URL`, `DATABASE_URL_TEST`). Vedere +`.env.example` per il formato atteso e i valori di default usati in sviluppo. + +## Sviluppo locale + +```bash +npm install +npx prisma migrate dev +npm run db:seed +npm run dev +``` + +Il server si avvia sulla porta definita da `PORT` (default 8080) e risponde +su `GET /health`. + +## Test + +```bash +npm test +``` + +Richiede `DATABASE_URL_TEST` configurato in `.env` e puntato a un database +PostgreSQL dedicato ai test, con le migration gia' applicate: la suite +esegue un `TRUNCATE` delle tabelle tra un test e l'altro, quindi va tenuto +separato dal database di sviluppo. + +## Note + +- L'autenticazione reale non e' ancora implementata: l'autore delle attivita' + e' ancora hardcoded a `"Movio"` nel router privato, e il middleware + `authPlaceholder` e' un pass-through. +- Porta e CORS sono gia' letti da variabili d'ambiente in previsione di un + futuro `docker-compose.yml` (non incluso in questa feature). + +## Riferimento storico + +La cartella `DDL/` contiene gli script SQL dello schema originale del +backend Java (MySQL), mantenuti come riferimento storico. Lo schema attuale +(PostgreSQL) e' definito in `prisma/schema.prisma` e gestito tramite Prisma +Migrate. diff --git a/scouthub-attivita-be/jest.config.ts b/scouthub-attivita-be/jest.config.ts new file mode 100644 index 0000000..5de9529 --- /dev/null +++ b/scouthub-attivita-be/jest.config.ts @@ -0,0 +1,16 @@ +import type { Config } from 'jest'; + +const config: Config = { + preset: 'ts-jest', + testEnvironment: 'node', + rootDir: '.', + testMatch: ['/tests/unit/**/*.test.ts', '/tests/integration/**/*.test.ts'], + globalSetup: '/tests/setup/globalSetup.ts', + globalTeardown: '/tests/setup/globalTeardown.ts', + testTimeout: 15000, + transform: { + '^.+\\.ts$': ['ts-jest', { tsconfig: 'tsconfig.jest.json' }], + }, +}; + +export default config; diff --git a/scouthub-attivita-be/package-lock.json b/scouthub-attivita-be/package-lock.json new file mode 100644 index 0000000..a0026ed --- /dev/null +++ b/scouthub-attivita-be/package-lock.json @@ -0,0 +1,6908 @@ +{ + "name": "scouthub-attivita-be", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "scouthub-attivita-be", + "version": "1.0.0", + "dependencies": { + "@prisma/client": "^5.16.1", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "jsonwebtoken": "^9.0.3", + "jwks-rsa": "^3.2.2", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21", + "@types/jest": "^30.0.0", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^20.14.12", + "@types/supertest": "^7.2.1", + "jest": "^30.4.2", + "nock": "^13.5.4", + "prisma": "^5.16.1", + "supertest": "^7.2.2", + "ts-jest": "^29.4.12", + "ts-node": "^10.9.2", + "ts-node-dev": "^2.0.0", + "typescript": "^5.5.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz", + "integrity": "sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/pattern": "30.4.0", + "@jest/reporters": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.4.1", + "jest-config": "30.4.2", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "jest-watcher": "30.4.1", + "pretty-format": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz", + "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.4.1", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz", + "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@sinonjs/fake-timers": "^15.4.0", + "@types/node": "*", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz", + "integrity": "sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/types": "30.4.1", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz", + "integrity": "sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz", + "integrity": "sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jest/test-result": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz", + "integrity": "sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/types": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz", + "integrity": "sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz", + "integrity": "sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jest/types": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@prisma/client": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", + "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", + "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/fetch-engine": "5.22.0", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", + "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", + "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", + "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/superagent": { + "version": "8.1.11", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.11.tgz", + "integrity": "sha512-KA7srSW/HENDtOw9DOqaFLgWuMqN9WgjEw62lh9dpvRaZDkhdOkazASd7X7i2eMUYLHa1U37ZttnePsH5zTDHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/supertest": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-7.2.1.tgz", + "integrity": "sha512-4CbBvoYVLHL7+yhbYrZET0vsvuyXTC05aRe7dNQkwMzm56auceoy6Yu3K50uZmwfHna1os3CMSgM/3QVkUtPTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz", + "integrity": "sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.4.1", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.4.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz", + "integrity": "sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz", + "integrity": "sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.4.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.1.tgz", + "integrity": "sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.395", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.395.tgz", + "integrity": "sha512-7zt9Aw+SrmxLWLN0zhaTWZQiCdryLVrYTq5R7iZakLvi2UQPYMMsROYV/2qVCzMeCiSXHwKOU+sZ4zOVVlrtKA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.4.2", + "@jest/types": "30.4.1", + "import-local": "^3.2.0", + "jest-cli": "30.4.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.4.1.tgz", + "integrity": "sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0", + "pretty-format": "30.4.1", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.4.2", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.4.0", + "@jest/test-sequencer": "30.4.1", + "@jest/types": "30.4.1", + "babel-jest": "30.4.1", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.4.2", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-runner": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "parse-json": "^5.2.0", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-diff": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.4.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz", + "integrity": "sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz", + "integrity": "sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "jest-util": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz", + "integrity": "sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz", + "integrity": "sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "picomatch": "^4.0.3", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-haste-map/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz", + "integrity": "sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.4.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", + "picomatch": "^4.0.3", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-mock": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz", + "integrity": "sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.4.0", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/environment": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-haste-map": "30.4.1", + "jest-leak-detector": "30.4.1", + "jest-message-util": "30.4.1", + "jest-resolve": "30.4.1", + "jest-runtime": "30.4.2", + "jest-util": "30.4.1", + "jest-watcher": "30.4.1", + "jest-worker": "30.4.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/globals": "30.4.1", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz", + "integrity": "sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-diff": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "pretty-format": "30.4.1", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz", + "integrity": "sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz", + "integrity": "sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.4.1", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz", + "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.4.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.2.tgz", + "integrity": "sha512-BqTyEDV+lS8F2trk3A+qJnxV5Q9EqKCBJOPti3W97r7qTympCZjb7h2X6f2kc+0K3rsSTY1/6YG2eaXKoj497w==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "^9.0.4", + "debug": "^4.3.4", + "jose": "^4.15.4", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jwks-rsa/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/jwks-rsa/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "license": "MIT", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nock": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/nock/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nock/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prisma": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", + "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/engines": "5.22.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is-18": { + "name": "react-is", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-is-19": { + "name": "react-is", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/superagent": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", + "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.1", + "cookiejar": "^2.1.4", + "debug": "^4.3.7", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.5", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.14.1" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/supertest": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", + "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie-signature": "^1.2.2", + "methods": "^1.1.2", + "superagent": "^10.3.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supertest/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", + "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.3.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-jest": { + "version": "29.4.12", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.12.tgz", + "integrity": "sha512-Ov6ClY53Fflh6BGAnY2DlTq1hYDrTycz2PVTXBWFW2CU+9zrEqAp9fWdGXl42EXO5RLSFAcAZ2JFKbP+zBTFfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.8.5", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node-dev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", + "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.1", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.6", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^10.4.0", + "tsconfig": "^7.0.0" + }, + "bin": { + "ts-node-dev": "lib/bin.js", + "tsnd": "lib/bin.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "node-notifier": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/scouthub-attivita-be/package.json b/scouthub-attivita-be/package.json new file mode 100644 index 0000000..8ac049c --- /dev/null +++ b/scouthub-attivita-be/package.json @@ -0,0 +1,47 @@ +{ + "name": "scouthub-attivita-be", + "version": "1.0.0", + "private": true, + "description": "Backend Node.js/TypeScript per la gestione di attivita scout", + "engines": { + "node": ">=18" + }, + "scripts": { + "dev": "ts-node-dev --respawn --transpile-only src/server.ts", + "build": "tsc -p tsconfig.json", + "start": "node dist/server.js", + "prisma:generate": "prisma generate", + "prisma:migrate": "prisma migrate dev", + "db:seed": "prisma db seed", + "test": "jest --runInBand --passWithNoTests", + "test:watch": "jest --watch --runInBand" + }, + "dependencies": { + "@prisma/client": "^5.16.1", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "jsonwebtoken": "^9.0.3", + "jwks-rsa": "^3.2.2", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21", + "@types/jest": "^30.0.0", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^20.14.12", + "@types/supertest": "^7.2.1", + "jest": "^30.4.2", + "nock": "^13.5.4", + "prisma": "^5.16.1", + "supertest": "^7.2.2", + "ts-jest": "^29.4.12", + "ts-node": "^10.9.2", + "ts-node-dev": "^2.0.0", + "typescript": "^5.5.4" + }, + "prisma": { + "seed": "ts-node prisma/seed.ts" + } +} diff --git a/scouthub-attivita-be/prisma/.gitkeep b/scouthub-attivita-be/prisma/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scouthub-attivita-be/prisma/migrations/20260722231426_init/migration.sql b/scouthub-attivita-be/prisma/migrations/20260722231426_init/migration.sql new file mode 100644 index 0000000..69abbac --- /dev/null +++ b/scouthub-attivita-be/prisma/migrations/20260722231426_init/migration.sql @@ -0,0 +1,223 @@ +-- CreateTable +CREATE TABLE "stato_attivita" ( + "id" VARCHAR(2) NOT NULL, + "nome" TEXT NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "stato_attivita_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "tipo_categoria" ( + "id" TEXT NOT NULL, + "nome" TEXT NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "tipo_categoria_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "tipo_paragrafo" ( + "id" TEXT NOT NULL, + "nome" TEXT NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "tipo_paragrafo_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "attivita" ( + "id" SERIAL NOT NULL, + "nome" TEXT NOT NULL, + "autore" TEXT NOT NULL, + "padre" INTEGER, + "stato" TEXT NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "attivita_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "branca" ( + "id" SERIAL NOT NULL, + "nome" TEXT NOT NULL, + "inizio_eta" INTEGER, + "fine_eta" INTEGER, + "colore" VARCHAR(10), + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "branca_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "branca_attivita" ( + "attivita_id" INTEGER NOT NULL, + "branca_id" INTEGER NOT NULL, + "cancellato" BOOLEAN NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "branca_attivita_pkey" PRIMARY KEY ("attivita_id","branca_id") +); + +-- CreateTable +CREATE TABLE "periodo_anno" ( + "id" SERIAL NOT NULL, + "nome" TEXT NOT NULL, + "inizio_mese" INTEGER, + "fine_mese" INTEGER, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "periodo_anno_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "periodo_anno_attivita" ( + "attivita_id" INTEGER NOT NULL, + "periodo_anno_id" INTEGER NOT NULL, + "cancellato" BOOLEAN NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "periodo_anno_attivita_pkey" PRIMARY KEY ("attivita_id","periodo_anno_id") +); + +-- CreateTable +CREATE TABLE "paragrafo" ( + "id" SERIAL NOT NULL, + "attivita_id" INTEGER NOT NULL, + "corpo" VARCHAR(500) NOT NULL, + "autore" TEXT NOT NULL, + "tipo" TEXT NOT NULL, + "ordine" INTEGER NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "paragrafo_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "categoria" ( + "id" SERIAL NOT NULL, + "nome" TEXT NOT NULL, + "padre" INTEGER, + "tipo" TEXT NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "categoria_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "categoria_attivita" ( + "attivita_id" INTEGER NOT NULL, + "categoria_id" INTEGER NOT NULL, + "cancellato" BOOLEAN NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "categoria_attivita_pkey" PRIMARY KEY ("attivita_id","categoria_id") +); + +-- CreateTable +CREATE TABLE "materiale" ( + "id" SERIAL NOT NULL, + "nome" TEXT NOT NULL, + "proprieta" JSONB, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "materiale_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "materiale_attivita" ( + "attivita_id" INTEGER NOT NULL, + "materiale_id" INTEGER NOT NULL, + "proprieta" JSONB, + "cancellato" BOOLEAN NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "materiale_attivita_pkey" PRIMARY KEY ("attivita_id","materiale_id") +); + +-- CreateTable +CREATE TABLE "categoria_materiale" ( + "materiale_id" INTEGER NOT NULL, + "categoria_id" INTEGER NOT NULL, + "cancellato" BOOLEAN NOT NULL, + "data_creazione" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "data_modifica" TIMESTAMP(3) NOT NULL, + "utente_modifica" TEXT NOT NULL, + + CONSTRAINT "categoria_materiale_pkey" PRIMARY KEY ("materiale_id","categoria_id") +); + +-- AddForeignKey +ALTER TABLE "attivita" ADD CONSTRAINT "attivita_padre_fkey" FOREIGN KEY ("padre") REFERENCES "attivita"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "attivita" ADD CONSTRAINT "attivita_stato_fkey" FOREIGN KEY ("stato") REFERENCES "stato_attivita"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "branca_attivita" ADD CONSTRAINT "branca_attivita_attivita_id_fkey" FOREIGN KEY ("attivita_id") REFERENCES "attivita"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "branca_attivita" ADD CONSTRAINT "branca_attivita_branca_id_fkey" FOREIGN KEY ("branca_id") REFERENCES "branca"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "periodo_anno_attivita" ADD CONSTRAINT "periodo_anno_attivita_attivita_id_fkey" FOREIGN KEY ("attivita_id") REFERENCES "attivita"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "periodo_anno_attivita" ADD CONSTRAINT "periodo_anno_attivita_periodo_anno_id_fkey" FOREIGN KEY ("periodo_anno_id") REFERENCES "periodo_anno"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "paragrafo" ADD CONSTRAINT "paragrafo_attivita_id_fkey" FOREIGN KEY ("attivita_id") REFERENCES "attivita"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "paragrafo" ADD CONSTRAINT "paragrafo_tipo_fkey" FOREIGN KEY ("tipo") REFERENCES "tipo_paragrafo"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "categoria" ADD CONSTRAINT "categoria_padre_fkey" FOREIGN KEY ("padre") REFERENCES "categoria"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "categoria" ADD CONSTRAINT "categoria_tipo_fkey" FOREIGN KEY ("tipo") REFERENCES "tipo_categoria"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "categoria_attivita" ADD CONSTRAINT "categoria_attivita_attivita_id_fkey" FOREIGN KEY ("attivita_id") REFERENCES "attivita"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "categoria_attivita" ADD CONSTRAINT "categoria_attivita_categoria_id_fkey" FOREIGN KEY ("categoria_id") REFERENCES "categoria"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "materiale_attivita" ADD CONSTRAINT "materiale_attivita_attivita_id_fkey" FOREIGN KEY ("attivita_id") REFERENCES "attivita"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "materiale_attivita" ADD CONSTRAINT "materiale_attivita_materiale_id_fkey" FOREIGN KEY ("materiale_id") REFERENCES "materiale"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "categoria_materiale" ADD CONSTRAINT "categoria_materiale_materiale_id_fkey" FOREIGN KEY ("materiale_id") REFERENCES "materiale"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "categoria_materiale" ADD CONSTRAINT "categoria_materiale_categoria_id_fkey" FOREIGN KEY ("categoria_id") REFERENCES "categoria"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + diff --git a/scouthub-attivita-be/prisma/migrations/20260723130000_add_autore_id/migration.sql b/scouthub-attivita-be/prisma/migrations/20260723130000_add_autore_id/migration.sql new file mode 100644 index 0000000..dd0530a --- /dev/null +++ b/scouthub-attivita-be/prisma/migrations/20260723130000_add_autore_id/migration.sql @@ -0,0 +1,7 @@ +-- AlterTable +-- Le righe esistenti (dati di sviluppo/seed, precedenti all'introduzione +-- dell'autenticazione Keycloak) non hanno un autore reale: valorizzate con +-- stringa vuota, poi il default viene rimosso perche' da qui in avanti +-- autoreId e' sempre valorizzato dal token (sub) al momento della creazione. +ALTER TABLE "attivita" ADD COLUMN "autore_id" TEXT NOT NULL DEFAULT ''; +ALTER TABLE "attivita" ALTER COLUMN "autore_id" DROP DEFAULT; diff --git a/scouthub-attivita-be/prisma/migrations/migration_lock.toml b/scouthub-attivita-be/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..99e4f20 --- /dev/null +++ b/scouthub-attivita-be/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" diff --git a/scouthub-attivita-be/prisma/schema.prisma b/scouthub-attivita-be/prisma/schema.prisma new file mode 100644 index 0000000..f00f980 --- /dev/null +++ b/scouthub-attivita-be/prisma/schema.prisma @@ -0,0 +1,223 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model StatoAttivita { + id String @id @db.VarChar(2) + nome String + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + attivita Attivita[] + + @@map("stato_attivita") +} + +model TipoCategoria { + id String @id + nome String + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + categorie Categoria[] + + @@map("tipo_categoria") +} + +model TipoParagrafo { + id String @id + nome String + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + paragrafi Paragrafo[] + + @@map("tipo_paragrafo") +} + +model Attivita { + id Int @id @default(autoincrement()) + nome String + autore String + autoreId String @map("autore_id") + padreId Int? @map("padre") + statoId String @map("stato") + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + padre Attivita? @relation("AttivitaPadre", fields: [padreId], references: [id]) + figli Attivita[] @relation("AttivitaPadre") + stato StatoAttivita @relation(fields: [statoId], references: [id]) + + paragrafi Paragrafo[] + brancaLinks BrancaAttivita[] + categoriaLinks CategoriaAttivita[] + materialeLinks MaterialeAttivita[] + periodoAnnoLinks PeriodoAnnoAttivita[] + + @@map("attivita") +} + +model Branca { + id Int @id @default(autoincrement()) + nome String + inizioEta Int? @map("inizio_eta") + fineEta Int? @map("fine_eta") + colore String? @db.VarChar(10) + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + attivitaLinks BrancaAttivita[] + + @@map("branca") +} + +model BrancaAttivita { + attivitaId Int @map("attivita_id") + brancaId Int @map("branca_id") + cancellato Boolean + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + attivita Attivita @relation(fields: [attivitaId], references: [id]) + branca Branca @relation(fields: [brancaId], references: [id]) + + @@id([attivitaId, brancaId]) + @@map("branca_attivita") +} + +model PeriodoAnno { + id Int @id @default(autoincrement()) + nome String + inizioMese Int? @map("inizio_mese") + fineMese Int? @map("fine_mese") + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + attivitaLinks PeriodoAnnoAttivita[] + + @@map("periodo_anno") +} + +model PeriodoAnnoAttivita { + attivitaId Int @map("attivita_id") + periodoAnnoId Int @map("periodo_anno_id") + cancellato Boolean + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + attivita Attivita @relation(fields: [attivitaId], references: [id]) + periodoAnno PeriodoAnno @relation(fields: [periodoAnnoId], references: [id]) + + @@id([attivitaId, periodoAnnoId]) + @@map("periodo_anno_attivita") +} + +model Paragrafo { + id Int @id @default(autoincrement()) + attivitaId Int @map("attivita_id") + corpo String @db.VarChar(500) + autore String + tipoId String @map("tipo") + ordine Int + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + attivita Attivita @relation(fields: [attivitaId], references: [id]) + tipo TipoParagrafo @relation(fields: [tipoId], references: [id]) + + @@map("paragrafo") +} + +model Categoria { + id Int @id @default(autoincrement()) + nome String + padreId Int? @map("padre") + tipoId String @map("tipo") + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + padre Categoria? @relation("CategoriaPadre", fields: [padreId], references: [id]) + figli Categoria[] @relation("CategoriaPadre") + tipo TipoCategoria @relation(fields: [tipoId], references: [id]) + + attivitaLinks CategoriaAttivita[] + materialeLinks CategoriaMateriale[] + + @@map("categoria") +} + +model CategoriaAttivita { + attivitaId Int @map("attivita_id") + categoriaId Int @map("categoria_id") + cancellato Boolean + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + attivita Attivita @relation(fields: [attivitaId], references: [id]) + categoria Categoria @relation(fields: [categoriaId], references: [id]) + + @@id([attivitaId, categoriaId]) + @@map("categoria_attivita") +} + +model Materiale { + id Int @id @default(autoincrement()) + nome String + proprieta Json? + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + attivitaLinks MaterialeAttivita[] + categoriaLinks CategoriaMateriale[] + + @@map("materiale") +} + +model MaterialeAttivita { + attivitaId Int @map("attivita_id") + materialeId Int @map("materiale_id") + proprieta Json? + cancellato Boolean + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + attivita Attivita @relation(fields: [attivitaId], references: [id]) + materiale Materiale @relation(fields: [materialeId], references: [id]) + + @@id([attivitaId, materialeId]) + @@map("materiale_attivita") +} + +model CategoriaMateriale { + materialeId Int @map("materiale_id") + categoriaId Int @map("categoria_id") + cancellato Boolean + dataCreazione DateTime @default(now()) @map("data_creazione") + dataModifica DateTime @updatedAt @map("data_modifica") + utenteModifica String @map("utente_modifica") + + materiale Materiale @relation(fields: [materialeId], references: [id]) + categoria Categoria @relation(fields: [categoriaId], references: [id]) + + @@id([materialeId, categoriaId]) + @@map("categoria_materiale") +} diff --git a/scouthub-attivita-be/prisma/seed.ts b/scouthub-attivita-be/prisma/seed.ts new file mode 100644 index 0000000..222eb6d --- /dev/null +++ b/scouthub-attivita-be/prisma/seed.ts @@ -0,0 +1,114 @@ +import { PrismaClient } from "@prisma/client"; + +const prisma = new PrismaClient(); + +const UTENTE_MODIFICA = "MANUALE"; + +async function main() { + await Promise.all( + [ + { id: "PU", nome: "Pubblicato" }, + { id: "BO", nome: "Bozza" }, + { id: "PR", nome: "Privato" }, + ].map((stato) => + prisma.statoAttivita.upsert({ + where: { id: stato.id }, + update: { nome: stato.nome, utenteModifica: UTENTE_MODIFICA }, + create: { ...stato, utenteModifica: UTENTE_MODIFICA }, + }) + ) + ); + + await Promise.all( + [ + { id: "A", nome: "Attività" }, + { id: "M", nome: "Materiale" }, + ].map((tipo) => + prisma.tipoCategoria.upsert({ + where: { id: tipo.id }, + update: { nome: tipo.nome, utenteModifica: UTENTE_MODIFICA }, + create: { ...tipo, utenteModifica: UTENTE_MODIFICA }, + }) + ) + ); + + await Promise.all( + [ + { id: "TITOLO", nome: "Titolo attività" }, + { id: "PARAGRAFO", nome: "Paragrafo attività" }, + ].map((tipo) => + prisma.tipoParagrafo.upsert({ + where: { id: tipo.id }, + update: { nome: tipo.nome, utenteModifica: UTENTE_MODIFICA }, + create: { ...tipo, utenteModifica: UTENTE_MODIFICA }, + }) + ) + ); + + await Promise.all( + [ + { id: 1, nome: "L/C", inizioEta: 8, fineEta: 10, colore: "#FDD835" }, + { id: 2, nome: "E/G", inizioEta: 11, fineEta: 16, colore: "#43A047" }, + { id: 3, nome: "R/S", inizioEta: 17, fineEta: 21, colore: "#E53935" }, + { id: 4, nome: "Co.Ca.", inizioEta: 22, fineEta: 99, colore: "#8E24AA" }, + ].map((branca) => + prisma.branca.upsert({ + where: { id: branca.id }, + update: { ...branca, utenteModifica: UTENTE_MODIFICA }, + create: { ...branca, utenteModifica: UTENTE_MODIFICA }, + }) + ) + ); + + await Promise.all( + [ + { id: 1, nome: "promessa", inizioMese: 1, fineMese: 3 }, + { id: 2, nome: "campo estivo", inizioMese: 6, fineMese: 9 }, + { id: 3, nome: "campo invernale", inizioMese: 12, fineMese: 1 }, + ].map((periodo) => + prisma.periodoAnno.upsert({ + where: { id: periodo.id }, + update: { ...periodo, utenteModifica: UTENTE_MODIFICA }, + create: { ...periodo, utenteModifica: UTENTE_MODIFICA }, + }) + ) + ); + + const categorieSenzaPadre = [ + { id: 1, nome: "attività", tipoId: "A" }, + { id: 2, nome: "gioco", tipoId: "A" }, + { id: 3, nome: "danza", tipoId: "A" }, + ]; + for (const categoria of categorieSenzaPadre) { + await prisma.categoria.upsert({ + where: { id: categoria.id }, + update: { ...categoria, padreId: null, utenteModifica: UTENTE_MODIFICA }, + create: { ...categoria, utenteModifica: UTENTE_MODIFICA }, + }); + } + + const categorieConPadre = [ + { id: 4, nome: "gioco d'acqua", tipoId: "A", padreId: 2 }, + { id: 5, nome: "gioco notturno", tipoId: "A", padreId: 2 }, + { id: 6, nome: "grande gioco", tipoId: "A", padreId: 2 }, + { id: 7, nome: "torneo", tipoId: "A", padreId: 2 }, + { id: 8, nome: "olimpiadi", tipoId: "A", padreId: 2 }, + { id: 9, nome: "gioco giungla", tipoId: "A", padreId: 2 }, + ]; + for (const categoria of categorieConPadre) { + await prisma.categoria.upsert({ + where: { id: categoria.id }, + update: { ...categoria, utenteModifica: UTENTE_MODIFICA }, + create: { ...categoria, utenteModifica: UTENTE_MODIFICA }, + }); + } +} + +main() + .catch((error) => { + console.error(error); + process.exitCode = 1; + }) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/scouthub-attivita-be/src/app.ts b/scouthub-attivita-be/src/app.ts new file mode 100644 index 0000000..9796510 --- /dev/null +++ b/scouthub-attivita-be/src/app.ts @@ -0,0 +1,30 @@ +import express from 'express'; +import cors from 'cors'; +import { env } from './config/env'; +import { authenticate } from './middlewares/authenticate'; +import { errorHandler } from './middlewares/errorHandler'; +import { attivitaPublicRouter } from './modules/attivita/attivita.router.public'; +import { attivitaPrivateRouter } from './modules/attivita/attivita.router.private'; +import { autocompleteRouter } from './modules/autocomplete/autocomplete.router'; + +export const app = express(); + +// strict: false perché gli endpoint di autocomplete (vedi extractKeyword) +// accettano un body JSON che è una stringa "nuda" (es. `"gioco"`), non solo +// oggetti/array: con lo strict mode di default body-parser la rifiuterebbe. +app.use(express.json({ strict: false })); +app.use(cors({ origin: env.corsOrigin })); + +app.get('/health', (req, res) => { + res.json({ status: 'ok' }); +}); + +app.use('/public/attivita', attivitaPublicRouter); +app.use('/private/attivita', authenticate, attivitaPrivateRouter); +app.use('/public/autocomplete', autocompleteRouter); + +app.use((req, res) => { + res.status(404).json({ message: 'not found' }); +}); + +app.use(errorHandler); diff --git a/scouthub-attivita-be/src/config/env.ts b/scouthub-attivita-be/src/config/env.ts new file mode 100644 index 0000000..fa4be1c --- /dev/null +++ b/scouthub-attivita-be/src/config/env.ts @@ -0,0 +1,21 @@ +import dotenv from 'dotenv'; + +dotenv.config(); + +function requireEnv(name: string): string { + const value = process.env[name]; + if (!value) { + throw new Error(`Variabile d'ambiente mancante: ${name}`); + } + return value; +} + +export const env = { + port: Number(process.env.PORT) || 8080, + corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:4200', + databaseUrl: requireEnv('DATABASE_URL'), + keycloak: { + baseUrl: requireEnv('KEYCLOAK_BASE_URL'), + realm: requireEnv('KEYCLOAK_REALM'), + }, +}; diff --git a/scouthub-attivita-be/src/db/prisma.ts b/scouthub-attivita-be/src/db/prisma.ts new file mode 100644 index 0000000..5eaa431 --- /dev/null +++ b/scouthub-attivita-be/src/db/prisma.ts @@ -0,0 +1,12 @@ +import { PrismaClient } from '@prisma/client'; + +declare global { + // eslint-disable-next-line no-var + var __prisma: PrismaClient | undefined; +} + +export const prisma = global.__prisma ?? new PrismaClient(); + +if (process.env.NODE_ENV !== 'production') { + global.__prisma = prisma; +} diff --git a/scouthub-attivita-be/src/errors.ts b/scouthub-attivita-be/src/errors.ts new file mode 100644 index 0000000..f39d1a8 --- /dev/null +++ b/scouthub-attivita-be/src/errors.ts @@ -0,0 +1,9 @@ +export class HttpError extends Error { + statusCode: number; + + constructor(statusCode: number, message: string) { + super(message); + this.statusCode = statusCode; + this.name = 'HttpError'; + } +} diff --git a/scouthub-attivita-be/src/middlewares/.gitkeep b/scouthub-attivita-be/src/middlewares/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scouthub-attivita-be/src/middlewares/auth.types.ts b/scouthub-attivita-be/src/middlewares/auth.types.ts new file mode 100644 index 0000000..7853fbb --- /dev/null +++ b/scouthub-attivita-be/src/middlewares/auth.types.ts @@ -0,0 +1,6 @@ +export interface AuthContext { + userId: string; + email: string | null; + name: string; + roles: string[]; +} diff --git a/scouthub-attivita-be/src/middlewares/authenticate.ts b/scouthub-attivita-be/src/middlewares/authenticate.ts new file mode 100644 index 0000000..37af898 --- /dev/null +++ b/scouthub-attivita-be/src/middlewares/authenticate.ts @@ -0,0 +1,67 @@ +import { Request, Response, NextFunction } from 'express'; +import jwt, { JwtPayload } from 'jsonwebtoken'; +import jwksClient from 'jwks-rsa'; +import { env } from '../config/env'; +import { AuthContext } from './auth.types'; + +const client = jwksClient({ + jwksUri: `${env.keycloak.baseUrl}/realms/${env.keycloak.realm}/protocol/openid-connect/certs`, + cache: true, + rateLimit: true, +}); + +interface KeycloakTokenPayload extends JwtPayload { + sub: string; + email?: string; + name?: string; + preferred_username?: string; + realm_access?: { roles?: string[] }; +} + +function extractBearerToken(req: Request): string | null { + const header = req.headers.authorization; + if (!header) { + return null; + } + const [scheme, token] = header.split(' '); + if (scheme !== 'Bearer' || !token) { + return null; + } + return token; +} + +function buildAuthContext(payload: KeycloakTokenPayload): AuthContext { + return { + userId: payload.sub, + email: payload.email ?? null, + name: payload.name ?? payload.preferred_username ?? payload.email ?? payload.sub, + roles: payload.realm_access?.roles ?? [], + }; +} + +export async function authenticate(req: Request, res: Response, next: NextFunction): Promise { + const token = extractBearerToken(req); + if (!token) { + res.status(401).json({ message: 'Token mancante' }); + return; + } + + try { + const decoded = jwt.decode(token, { complete: true }); + if (!decoded || !decoded.header.kid) { + throw new Error("Header del token privo di 'kid'"); + } + + const signingKey = await client.getSigningKey(decoded.header.kid); + const payload = jwt.verify(token, signingKey.getPublicKey(), { algorithms: ['RS256'] }); + + if (typeof payload === 'string') { + throw new Error('Payload del token non valido'); + } + + req.auth = buildAuthContext(payload as KeycloakTokenPayload); + next(); + } catch { + res.status(401).json({ message: 'Token non valido' }); + } +} diff --git a/scouthub-attivita-be/src/middlewares/errorHandler.ts b/scouthub-attivita-be/src/middlewares/errorHandler.ts new file mode 100644 index 0000000..d7f3256 --- /dev/null +++ b/scouthub-attivita-be/src/middlewares/errorHandler.ts @@ -0,0 +1,10 @@ +import { Request, Response, NextFunction } from 'express'; + +export function errorHandler(err: unknown, req: Request, res: Response, next: NextFunction): void { + const hasStatusCode = + typeof err === 'object' && err !== null && 'statusCode' in err && typeof (err as { statusCode?: unknown }).statusCode === 'number'; + const statusCode = hasStatusCode ? (err as { statusCode: number }).statusCode : 500; + const message = err instanceof Error && err.message ? err.message : 'Errore interno del server'; + + res.status(statusCode).json({ message }); +} diff --git a/scouthub-attivita-be/src/modules/attivita/.gitkeep b/scouthub-attivita-be/src/modules/attivita/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scouthub-attivita-be/src/modules/attivita/attivita.router.private.ts b/scouthub-attivita-be/src/modules/attivita/attivita.router.private.ts new file mode 100644 index 0000000..b29f8c5 --- /dev/null +++ b/scouthub-attivita-be/src/modules/attivita/attivita.router.private.ts @@ -0,0 +1,51 @@ +import { Request, Response, NextFunction, Router } from 'express'; +import { HttpError } from '../../errors'; +import { attivitaSaveSchema } from '../../types/validation'; +import * as attivitaService from './attivita.service'; + +function asyncHandler( + handler: (req: Request, res: Response, next: NextFunction) => Promise, +) { + return (req: Request, res: Response, next: NextFunction): void => { + handler(req, res, next).catch(next); + }; +} + +export const attivitaPrivateRouter = Router(); + +attivitaPrivateRouter.get( + '/get/lista/my', + asyncHandler(async (req, res) => { + const lista = await attivitaService.getListMy(req.auth!.userId); + res.status(200).json(lista); + }), +); + +attivitaPrivateRouter.get( + '/change/stato/:idAttivita/:idStato', + asyncHandler(async (req, res, next) => { + const idAttivita = Number(req.params.idAttivita); + if (!Number.isInteger(idAttivita)) { + next(new HttpError(400, "l'idAttivita deve essere un numero intero")); + return; + } + + const idStato = req.params.idStato; + const stato = await attivitaService.changeStato(idAttivita, idStato, req.auth!); + res.status(200).json(stato); + }), +); + +attivitaPrivateRouter.post( + '/save', + asyncHandler(async (req, res, next) => { + const parsed = attivitaSaveSchema.safeParse(req.body); + if (!parsed.success) { + next(new HttpError(400, JSON.stringify(parsed.error.flatten()))); + return; + } + + await attivitaService.save(parsed.data, req.auth!); + res.status(200).end(); + }), +); diff --git a/scouthub-attivita-be/src/modules/attivita/attivita.router.public.ts b/scouthub-attivita-be/src/modules/attivita/attivita.router.public.ts new file mode 100644 index 0000000..963b77f --- /dev/null +++ b/scouthub-attivita-be/src/modules/attivita/attivita.router.public.ts @@ -0,0 +1,63 @@ +import { Request, Response, NextFunction, Router } from 'express'; +import { z } from 'zod'; +import { HttpError } from '../../errors'; +import * as attivitaService from './attivita.service'; + +const searchObjectSchema = z.array( + z.object({ + id: z.number().nullable(), + nome: z.string().nullable(), + gruppo: z.string(), + }), +); + +function asyncHandler( + handler: (req: Request, res: Response, next: NextFunction) => Promise, +) { + return (req: Request, res: Response, next: NextFunction): void => { + handler(req, res, next).catch(next); + }; +} + +export const attivitaPublicRouter = Router(); + +attivitaPublicRouter.get( + '/get/lista/home', + asyncHandler(async (req, res) => { + const lista = await attivitaService.getListaHome(); + res.status(200).json(lista); + }), +); + +attivitaPublicRouter.post( + '/get/lista/search', + asyncHandler(async (req, res, next) => { + const parsed = searchObjectSchema.safeParse(req.body); + if (!parsed.success) { + next(new HttpError(400, 'body non valido: atteso un array di SearchObjectDto')); + return; + } + + const lista = await attivitaService.getListaSearch(parsed.data); + res.status(200).json(lista); + }), +); + +attivitaPublicRouter.get( + '/get/one/:id', + asyncHandler(async (req, res, next) => { + const id = Number(req.params.id); + if (!Number.isInteger(id)) { + next(new HttpError(400, "l'id deve essere un numero intero")); + return; + } + + const attivita = await attivitaService.getOne(id); + if (!attivita) { + next(new HttpError(404, 'attività non trovata')); + return; + } + + res.status(200).json(attivita); + }), +); diff --git a/scouthub-attivita-be/src/modules/attivita/attivita.service.ts b/scouthub-attivita-be/src/modules/attivita/attivita.service.ts new file mode 100644 index 0000000..de1055b --- /dev/null +++ b/scouthub-attivita-be/src/modules/attivita/attivita.service.ts @@ -0,0 +1,560 @@ +import { Prisma, PrismaClient } from '@prisma/client'; +import { prisma } from '../../db/prisma'; +import { HttpError } from '../../errors'; +import { AuthContext } from '../../middlewares/auth.types'; +import { + AttivitaDto, + BrancaDto, + CategoriaDto, + MaterialeDto, + ParagrafoDto, + PeriodoAnnoDto, + SearchObjectDto, + TipologicaDto, +} from '../../types/dto'; +import { AttivitaSaveInput } from '../../types/validation'; + +const attivitaInclude = { + stato: true, + brancaLinks: { include: { branca: true } }, + categoriaLinks: { include: { categoria: { include: { tipo: true } } } }, + materialeLinks: { include: { materiale: true } }, + periodoAnnoLinks: { include: { periodoAnno: true } }, + paragrafi: { include: { tipo: true } }, +} satisfies Prisma.AttivitaInclude; + +type AttivitaWithRelations = Prisma.AttivitaGetPayload<{ include: typeof attivitaInclude }>; + +function toTipologicaDto(entity: { id: string; nome: string }): TipologicaDto { + return { id: entity.id, nome: entity.nome }; +} + +function toAttivitaDto(entity: AttivitaWithRelations): AttivitaDto { + const brancaList: BrancaDto[] = entity.brancaLinks + .filter((link) => !link.cancellato) + .map((link) => ({ + id: link.branca.id, + nome: link.branca.nome, + inizioEta: link.branca.inizioEta, + fineEta: link.branca.fineEta, + colore: link.branca.colore, + cancellato: false, + dataCreazione: link.branca.dataCreazione, + dataModifica: link.branca.dataModifica, + utenteModifica: link.branca.utenteModifica, + })); + + const categoriaList: CategoriaDto[] = entity.categoriaLinks + .filter((link) => !link.cancellato) + .map((link) => ({ + id: link.categoria.id, + nome: link.categoria.nome, + padre: link.categoria.padreId, + tipo: toTipologicaDto(link.categoria.tipo), + cancellato: false, + dataCreazione: link.categoria.dataCreazione, + dataModifica: link.categoria.dataModifica, + utenteModifica: link.categoria.utenteModifica, + })); + + const materialeList: MaterialeDto[] = entity.materialeLinks + .filter((link) => !link.cancellato) + .map((link) => ({ + id: link.materiale.id, + nome: link.materiale.nome, + proprieta: link.proprieta, + cancellato: false, + dataCreazione: link.materiale.dataCreazione, + dataModifica: link.materiale.dataModifica, + utenteModifica: link.materiale.utenteModifica, + })); + + const periodoAnnoList: PeriodoAnnoDto[] = entity.periodoAnnoLinks + .filter((link) => !link.cancellato) + .map((link) => ({ + id: link.periodoAnno.id, + nome: link.periodoAnno.nome, + inizioMese: link.periodoAnno.inizioMese, + fineMese: link.periodoAnno.fineMese, + cancellato: false, + dataCreazione: link.periodoAnno.dataCreazione, + dataModifica: link.periodoAnno.dataModifica, + utenteModifica: link.periodoAnno.utenteModifica, + })); + + const paragrafoList: ParagrafoDto[] = [...entity.paragrafi] + .sort((a, b) => a.ordine - b.ordine) + .map((paragrafo) => ({ + id: paragrafo.id, + attivitaId: paragrafo.attivitaId, + corpo: paragrafo.corpo, + autore: paragrafo.autore, + tipo: toTipologicaDto(paragrafo.tipo), + ordine: paragrafo.ordine, + dataCreazione: paragrafo.dataCreazione, + dataModifica: paragrafo.dataModifica, + utenteModifica: paragrafo.utenteModifica, + })); + + return { + id: entity.id, + nome: entity.nome, + autore: entity.autore, + padre: entity.padreId, + stato: toTipologicaDto(entity.stato), + brancaList, + categoriaList, + materialeList, + paragrafoList, + periodoAnnoList, + dataCreazione: entity.dataCreazione, + dataModifica: entity.dataModifica, + utenteModifica: entity.utenteModifica, + }; +} + +export async function getListaHome(): Promise { + const entities = await prisma.attivita.findMany({ + where: { statoId: 'PU' }, + include: attivitaInclude, + orderBy: { dataModifica: 'desc' }, + }); + + return entities.map(toAttivitaDto); +} + +export async function getOne(id: number): Promise { + const entity = await prisma.attivita.findUnique({ + where: { id }, + include: attivitaInclude, + }); + + return entity ? toAttivitaDto(entity) : null; +} + +export async function getListMy(autoreId: string): Promise { + const entities = await prisma.attivita.findMany({ + where: { autoreId }, + include: attivitaInclude, + orderBy: { dataModifica: 'desc' }, + }); + + return entities.map(toAttivitaDto); +} + +export async function getListaSearch(dtoList: SearchObjectDto[]): Promise { + const brancaIds = dtoList.filter((d) => d.gruppo === 'branca').map((d) => d.id as number); + const categoriaIds = dtoList.filter((d) => d.gruppo === 'categoria').map((d) => d.id as number); + const materialeIds = dtoList.filter((d) => d.gruppo === 'materiale').map((d) => d.id as number); + const periodoAnnoIds = dtoList + .filter((d) => d.gruppo === 'periodoAnno') + .map((d) => d.id as number); + const testoList = dtoList + .filter((d) => d.gruppo === 'testo') + .map((d) => d.nome) + .filter((nome): nome is string => !!nome); + + const and: Prisma.AttivitaWhereInput[] = []; + + if (brancaIds.length > 0) { + and.push({ + brancaLinks: { some: { brancaId: { in: brancaIds }, cancellato: false } }, + }); + } + + if (categoriaIds.length > 0) { + and.push({ + categoriaLinks: { some: { categoriaId: { in: categoriaIds }, cancellato: false } }, + }); + } + + if (materialeIds.length > 0) { + and.push({ + materialeLinks: { some: { materialeId: { in: materialeIds }, cancellato: false } }, + }); + } + + if (periodoAnnoIds.length > 0) { + and.push({ + periodoAnnoLinks: { some: { periodoAnnoId: { in: periodoAnnoIds }, cancellato: false } }, + }); + } + + if (testoList.length > 0) { + and.push({ + OR: testoList.flatMap((testo) => [ + { nome: { contains: testo, mode: 'insensitive' } }, + { paragrafi: { some: { corpo: { contains: testo, mode: 'insensitive' } } } }, + ]), + }); + } + + const entities = await prisma.attivita.findMany({ + where: and.length > 0 ? { AND: and } : {}, + include: attivitaInclude, + orderBy: { dataModifica: 'desc' }, + }); + + return entities.map(toAttivitaDto); +} + +export async function changeStato( + idAttivita: number, + idStato: string, + auth: AuthContext, +): Promise { + const existing = await prisma.attivita.findUnique({ where: { id: idAttivita } }); + if (!existing) { + throw new HttpError(404, 'attività non trovata'); + } + if (existing.autoreId !== auth.userId) { + throw new HttpError(403, 'non sei autore di questa attività'); + } + + const updated = await prisma.attivita.update({ + where: { id: idAttivita }, + data: { + statoId: idStato, + utenteModifica: auth.name, + }, + include: { stato: true }, + }); + + return toTipologicaDto(updated.stato); +} + +type Tx = Omit; + +async function upsertBranca( + tx: Tx, + attivitaId: number, + input: AttivitaSaveInput['brancaList'][number], + auth: AuthContext, +): Promise { + let brancaId = input.id; + + if (!brancaId) { + const found = await tx.branca.findFirst({ + where: { nome: { equals: input.nome, mode: 'insensitive' } }, + }); + + if (found) { + brancaId = found.id; + } else { + const created = await tx.branca.create({ + data: { + nome: input.nome, + utenteModifica: auth.name, + }, + }); + brancaId = created.id; + } + } + + const existingLink = await tx.brancaAttivita.findUnique({ + where: { attivitaId_brancaId: { attivitaId, brancaId } }, + }); + + if (existingLink) { + await tx.brancaAttivita.update({ + where: { attivitaId_brancaId: { attivitaId, brancaId } }, + data: { + cancellato: false, + utenteModifica: auth.name, + }, + }); + } else { + await tx.brancaAttivita.create({ + data: { + attivitaId, + brancaId, + cancellato: false, + utenteModifica: auth.name, + }, + }); + } + + return brancaId; +} + +async function upsertCategoria( + tx: Tx, + attivitaId: number, + input: AttivitaSaveInput['categoriaList'][number], + auth: AuthContext, +): Promise { + let categoriaId = input.id; + + if (!categoriaId) { + const found = await tx.categoria.findFirst({ + where: { nome: { equals: input.nome, mode: 'insensitive' } }, + }); + + if (found) { + categoriaId = found.id; + } else { + const created = await tx.categoria.create({ + data: { + nome: input.nome, + tipoId: 'A', + utenteModifica: auth.name, + }, + }); + categoriaId = created.id; + } + } + + const existingLink = await tx.categoriaAttivita.findUnique({ + where: { attivitaId_categoriaId: { attivitaId, categoriaId } }, + }); + + if (existingLink) { + await tx.categoriaAttivita.update({ + where: { attivitaId_categoriaId: { attivitaId, categoriaId } }, + data: { + cancellato: false, + utenteModifica: auth.name, + }, + }); + } else { + await tx.categoriaAttivita.create({ + data: { + attivitaId, + categoriaId, + cancellato: false, + utenteModifica: auth.name, + }, + }); + } + + return categoriaId; +} + +async function upsertMateriale( + tx: Tx, + attivitaId: number, + input: AttivitaSaveInput['materialeList'][number], + auth: AuthContext, +): Promise { + let materialeId = input.id; + + if (!materialeId) { + const found = await tx.materiale.findFirst({ + where: { nome: { equals: input.nome, mode: 'insensitive' } }, + }); + + if (found) { + materialeId = found.id; + } else { + const created = await tx.materiale.create({ + data: { + nome: input.nome, + utenteModifica: auth.name, + }, + }); + materialeId = created.id; + } + } + + const proprieta = (input.proprieta ?? Prisma.JsonNull) as Prisma.InputJsonValue; + + const existingLink = await tx.materialeAttivita.findUnique({ + where: { attivitaId_materialeId: { attivitaId, materialeId } }, + }); + + if (existingLink) { + await tx.materialeAttivita.update({ + where: { attivitaId_materialeId: { attivitaId, materialeId } }, + data: { + cancellato: false, + proprieta, + utenteModifica: auth.name, + }, + }); + } else { + await tx.materialeAttivita.create({ + data: { + attivitaId, + materialeId, + cancellato: false, + proprieta, + utenteModifica: auth.name, + }, + }); + } + + return materialeId; +} + +async function upsertPeriodoAnno( + tx: Tx, + attivitaId: number, + input: AttivitaSaveInput['periodoAnnoList'][number], + auth: AuthContext, +): Promise { + let periodoAnnoId = input.id; + + if (!periodoAnnoId) { + const found = await tx.periodoAnno.findFirst({ + where: { nome: { equals: input.nome, mode: 'insensitive' } }, + }); + + if (found) { + periodoAnnoId = found.id; + } else { + const created = await tx.periodoAnno.create({ + data: { + nome: input.nome, + utenteModifica: auth.name, + }, + }); + periodoAnnoId = created.id; + } + } + + const existingLink = await tx.periodoAnnoAttivita.findUnique({ + where: { attivitaId_periodoAnnoId: { attivitaId, periodoAnnoId } }, + }); + + if (existingLink) { + await tx.periodoAnnoAttivita.update({ + where: { attivitaId_periodoAnnoId: { attivitaId, periodoAnnoId } }, + data: { + cancellato: false, + utenteModifica: auth.name, + }, + }); + } else { + await tx.periodoAnnoAttivita.create({ + data: { + attivitaId, + periodoAnnoId, + cancellato: false, + utenteModifica: auth.name, + }, + }); + } + + return periodoAnnoId; +} + +export async function save(dto: AttivitaSaveInput, auth: AuthContext): Promise { + await prisma.$transaction(async (tx) => { + let attivitaId: number; + + if (dto.id) { + const existing = await tx.attivita.findUnique({ where: { id: dto.id } }); + if (!existing) { + throw new HttpError(404, 'attività non trovata'); + } + if (existing.autoreId !== auth.userId) { + throw new HttpError(403, 'non sei autore di questa attività'); + } + + await tx.attivita.update({ + where: { id: dto.id }, + data: { + nome: dto.nome, + statoId: dto.stato.id, + utenteModifica: auth.name, + }, + }); + attivitaId = dto.id; + } else { + const created = await tx.attivita.create({ + data: { + nome: dto.nome, + autore: auth.name, + autoreId: auth.userId, + statoId: dto.stato.id, + utenteModifica: auth.name, + }, + }); + attivitaId = created.id; + } + + const brancaIds = new Set(); + for (const branca of dto.brancaList) { + brancaIds.add(await upsertBranca(tx, attivitaId, branca, auth)); + } + await tx.brancaAttivita.updateMany({ + where: { attivitaId, brancaId: { notIn: [...brancaIds] }, cancellato: false }, + data: { + cancellato: true, + utenteModifica: auth.name, + }, + }); + + const categoriaIds = new Set(); + for (const categoria of dto.categoriaList) { + categoriaIds.add(await upsertCategoria(tx, attivitaId, categoria, auth)); + } + await tx.categoriaAttivita.updateMany({ + where: { attivitaId, categoriaId: { notIn: [...categoriaIds] }, cancellato: false }, + data: { + cancellato: true, + utenteModifica: auth.name, + }, + }); + + const materialeIds = new Set(); + for (const materiale of dto.materialeList) { + materialeIds.add(await upsertMateriale(tx, attivitaId, materiale, auth)); + } + await tx.materialeAttivita.updateMany({ + where: { attivitaId, materialeId: { notIn: [...materialeIds] }, cancellato: false }, + data: { + cancellato: true, + utenteModifica: auth.name, + }, + }); + + const periodoAnnoIds = new Set(); + for (const periodoAnno of dto.periodoAnnoList) { + periodoAnnoIds.add(await upsertPeriodoAnno(tx, attivitaId, periodoAnno, auth)); + } + await tx.periodoAnnoAttivita.updateMany({ + where: { attivitaId, periodoAnnoId: { notIn: [...periodoAnnoIds] }, cancellato: false }, + data: { + cancellato: true, + utenteModifica: auth.name, + }, + }); + + const paragrafoIds = new Set(); + for (const paragrafo of dto.paragrafoList) { + // Il paragrafo non ha un autore proprio nell'API: eredita quello dell'attivita' + // se non esplicitamente indicato nel payload. + const paragrafoAutore = paragrafo.autore ?? auth.name; + + if (paragrafo.id) { + await tx.paragrafo.update({ + where: { id: paragrafo.id }, + data: { + corpo: paragrafo.corpo, + autore: paragrafoAutore, + tipoId: paragrafo.tipo.id, + ordine: paragrafo.ordine, + utenteModifica: auth.name, + }, + }); + paragrafoIds.add(paragrafo.id); + } else { + const created = await tx.paragrafo.create({ + data: { + attivitaId, + corpo: paragrafo.corpo, + autore: paragrafoAutore, + tipoId: paragrafo.tipo.id, + ordine: paragrafo.ordine, + utenteModifica: auth.name, + }, + }); + paragrafoIds.add(created.id); + } + } + await tx.paragrafo.deleteMany({ + where: { attivitaId, id: { notIn: [...paragrafoIds] } }, + }); + }); +} diff --git a/scouthub-attivita-be/src/modules/autocomplete/.gitkeep b/scouthub-attivita-be/src/modules/autocomplete/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scouthub-attivita-be/src/modules/autocomplete/autocomplete.router.ts b/scouthub-attivita-be/src/modules/autocomplete/autocomplete.router.ts new file mode 100644 index 0000000..c4fecae --- /dev/null +++ b/scouthub-attivita-be/src/modules/autocomplete/autocomplete.router.ts @@ -0,0 +1,56 @@ +import { Request, Response, NextFunction, Router } from 'express'; +import * as autocompleteService from './autocomplete.service'; + +function asyncHandler( + handler: (req: Request, res: Response, next: NextFunction) => Promise, +) { + return (req: Request, res: Response, next: NextFunction): void => { + handler(req, res, next).catch(next); + }; +} + +function extractKeyword(body: unknown): string | undefined { + return typeof body === 'string' ? body : undefined; +} + +export const autocompleteRouter = Router(); + +autocompleteRouter.post( + '/get/search', + asyncHandler(async (req, res) => { + const groups = await autocompleteService.getSearch(extractKeyword(req.body)); + res.status(200).json(groups); + }), +); + +autocompleteRouter.post( + '/get/branca', + asyncHandler(async (req, res) => { + const lista = await autocompleteService.getBranca(extractKeyword(req.body)); + res.status(200).json(lista); + }), +); + +autocompleteRouter.post( + '/get/categoria', + asyncHandler(async (req, res) => { + const lista = await autocompleteService.getCategoria(extractKeyword(req.body)); + res.status(200).json(lista); + }), +); + +autocompleteRouter.post( + '/get/materiale', + asyncHandler(async (req, res) => { + const lista = await autocompleteService.getMateriale(extractKeyword(req.body)); + res.status(200).json(lista); + }), +); + +autocompleteRouter.post( + '/get/periodoAnno', + asyncHandler(async (req, res) => { + const lista = await autocompleteService.getPeriodoAnno(extractKeyword(req.body)); + res.status(200).json(lista); + }), +); diff --git a/scouthub-attivita-be/src/modules/autocomplete/autocomplete.service.ts b/scouthub-attivita-be/src/modules/autocomplete/autocomplete.service.ts new file mode 100644 index 0000000..fe82e6e --- /dev/null +++ b/scouthub-attivita-be/src/modules/autocomplete/autocomplete.service.ts @@ -0,0 +1,62 @@ +import { prisma } from '../../db/prisma'; +import { SearchGroupDto, SearchObjectDto } from '../../types/dto'; + +export async function getBranca(keyword?: string | null): Promise { + const entities = await prisma.branca.findMany({ + where: { nome: { contains: keyword ?? '', mode: 'insensitive' } }, + }); + + return entities.map((entity) => ({ id: entity.id, nome: entity.nome, gruppo: 'branca' })); +} + +export async function getCategoria(keyword?: string | null): Promise { + const entities = await prisma.categoria.findMany({ + where: { nome: { contains: keyword ?? '', mode: 'insensitive' } }, + }); + + return entities.map((entity) => ({ id: entity.id, nome: entity.nome, gruppo: 'categoria' })); +} + +export async function getMateriale(keyword?: string | null): Promise { + const entities = await prisma.materiale.findMany({ + where: { nome: { contains: keyword ?? '', mode: 'insensitive' } }, + }); + + return entities.map((entity) => ({ id: entity.id, nome: entity.nome, gruppo: 'materiale' })); +} + +export async function getPeriodoAnno(keyword?: string | null): Promise { + const entities = await prisma.periodoAnno.findMany({ + where: { nome: { contains: keyword ?? '', mode: 'insensitive' } }, + }); + + return entities.map((entity) => ({ id: entity.id, nome: entity.nome, gruppo: 'periodoAnno' })); +} + +export async function getSearch(keyword?: string | null): Promise { + const groups: SearchGroupDto[] = [ + { label: 'Testo', objectsList: [{ id: null, nome: keyword ?? null, gruppo: 'testo' }] }, + ]; + + const brancaList = await getBranca(keyword); + if (brancaList.length > 0) { + groups.push({ label: 'Branca', objectsList: brancaList }); + } + + const categoriaList = await getCategoria(keyword); + if (categoriaList.length > 0) { + groups.push({ label: 'Categoria', objectsList: categoriaList }); + } + + const materialeList = await getMateriale(keyword); + if (materialeList.length > 0) { + groups.push({ label: 'Materiale', objectsList: materialeList }); + } + + const periodoAnnoList = await getPeriodoAnno(keyword); + if (periodoAnnoList.length > 0) { + groups.push({ label: 'Periodo anno', objectsList: periodoAnnoList }); + } + + return groups; +} diff --git a/scouthub-attivita-be/src/server.ts b/scouthub-attivita-be/src/server.ts new file mode 100644 index 0000000..73ae4a6 --- /dev/null +++ b/scouthub-attivita-be/src/server.ts @@ -0,0 +1,6 @@ +import { app } from './app'; +import { env } from './config/env'; + +app.listen(env.port, () => { + console.log(`Server avviato su porta ${env.port} (CORS origin: ${env.corsOrigin})`); +}); diff --git a/scouthub-attivita-be/src/types/.gitkeep b/scouthub-attivita-be/src/types/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scouthub-attivita-be/src/types/dto.ts b/scouthub-attivita-be/src/types/dto.ts new file mode 100644 index 0000000..af188ab --- /dev/null +++ b/scouthub-attivita-be/src/types/dto.ts @@ -0,0 +1,76 @@ +export interface TipologicaDto { + id: string; + nome: string; +} + +export interface BaseDto { + dataCreazione: Date; + dataModifica: Date; + utenteModifica: string; +} + +export interface BrancaDto extends BaseDto { + id: number; + nome: string; + inizioEta: number | null; + fineEta: number | null; + colore: string | null; + cancellato: boolean; +} + +export interface CategoriaDto extends BaseDto { + id: number; + nome: string; + padre: number | null; + tipo: TipologicaDto; + cancellato: boolean; +} + +export interface MaterialeDto extends BaseDto { + id: number; + nome: string; + proprieta: unknown; + categoriaList?: CategoriaDto[]; + cancellato: boolean; +} + +export interface ParagrafoDto extends BaseDto { + id: number; + attivitaId: number; + corpo: string; + autore: string; + tipo: TipologicaDto; + ordine: number; +} + +export interface PeriodoAnnoDto extends BaseDto { + id: number; + nome: string; + inizioMese: number | null; + fineMese: number | null; + cancellato: boolean; +} + +export interface AttivitaDto extends BaseDto { + id: number | null; + nome: string; + autore: string; + padre: number | null; + stato: TipologicaDto; + brancaList: BrancaDto[]; + categoriaList: CategoriaDto[]; + materialeList: MaterialeDto[]; + paragrafoList: ParagrafoDto[]; + periodoAnnoList: PeriodoAnnoDto[]; +} + +export interface SearchObjectDto { + id: number | null; + nome: string | null; + gruppo: string; +} + +export interface SearchGroupDto { + label: string; + objectsList: SearchObjectDto[]; +} diff --git a/scouthub-attivita-be/src/types/express.d.ts b/scouthub-attivita-be/src/types/express.d.ts new file mode 100644 index 0000000..18de390 --- /dev/null +++ b/scouthub-attivita-be/src/types/express.d.ts @@ -0,0 +1,11 @@ +import { AuthContext } from '../middlewares/auth.types'; + +declare global { + namespace Express { + interface Request { + auth?: AuthContext; + } + } +} + +export {}; diff --git a/scouthub-attivita-be/src/types/validation.ts b/scouthub-attivita-be/src/types/validation.ts new file mode 100644 index 0000000..1cc13df --- /dev/null +++ b/scouthub-attivita-be/src/types/validation.ts @@ -0,0 +1,58 @@ +import { z } from 'zod'; + +export const tipologicaSchema = z.object({ + id: z.string(), + nome: z.string(), +}); + +export const paragrafoInputSchema = z.object({ + id: z.number().optional(), + corpo: z.string().min(1).max(500), + // Un paragrafo non ha un autore proprio nell'API/frontend: eredita quello + // dell'attivita' se non esplicitamente indicato (vedi attivita.service.ts). + autore: z.string().min(1).optional(), + tipo: z.object({ + id: z.string(), + }), + ordine: z.number().int(), +}); + +export const brancaInputSchema = z.object({ + id: z.number().optional(), + nome: z.string().min(1), +}); + +export const categoriaInputSchema = z.object({ + id: z.number().optional(), + nome: z.string().min(1), +}); + +export const materialeInputSchema = z.object({ + id: z.number().optional(), + nome: z.string().min(1), + proprieta: z.unknown().optional(), +}); + +export const periodoAnnoInputSchema = z.object({ + id: z.number().optional(), + nome: z.string().min(1), +}); + +export const attivitaSaveSchema = z.object({ + id: z.number().optional(), + nome: z.string().min(1), + stato: tipologicaSchema, + brancaList: z.array(brancaInputSchema), + categoriaList: z.array(categoriaInputSchema), + materialeList: z.array(materialeInputSchema), + periodoAnnoList: z.array(periodoAnnoInputSchema), + paragrafoList: z.array(paragrafoInputSchema), +}); + +export type TipologicaInput = z.infer; +export type ParagrafoInput = z.infer; +export type BrancaInput = z.infer; +export type CategoriaInput = z.infer; +export type MaterialeInput = z.infer; +export type PeriodoAnnoInput = z.infer; +export type AttivitaSaveInput = z.infer; diff --git a/scouthub-attivita-be/tests/.gitkeep b/scouthub-attivita-be/tests/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scouthub-attivita-be/tests/integration/attivita.endpoints.test.ts b/scouthub-attivita-be/tests/integration/attivita.endpoints.test.ts new file mode 100644 index 0000000..fc1d6a5 --- /dev/null +++ b/scouthub-attivita-be/tests/integration/attivita.endpoints.test.ts @@ -0,0 +1,192 @@ +import request from 'supertest'; +import nock from 'nock'; +import { app } from '../../src/app'; +import { prisma } from '../../src/db/prisma'; +import { resetDatabase } from '../setup/resetDatabase'; +import { bearer, mockKeycloakJwks } from '../setup/authTestHelper'; + +const AUTORE = { sub: 'user-movio', email: 'movio@example.com', name: 'Movio' }; +const ALTRO_AUTORE = { sub: 'user-altro', email: 'altro@example.com', name: 'Altro Utente' }; +const AUTH_HEADER = bearer(AUTORE); +const ALTRO_AUTH_HEADER = bearer(ALTRO_AUTORE); + +function attivitaPayload(overrides: Record = {}) { + return { + nome: 'Caccia al tesoro', + stato: { id: 'BO', nome: 'Bozza' }, + brancaList: [{ nome: 'E/G' }], + categoriaList: [{ nome: 'grande gioco' }], + materialeList: [], + periodoAnnoList: [], + paragrafoList: [{ corpo: 'Introduzione al gioco', tipo: { id: 'PARAGRAFO' }, ordine: 1 }], + ...overrides, + }; +} + +beforeAll(() => { + mockKeycloakJwks(); +}); + +afterAll(() => { + nock.cleanAll(); +}); + +beforeEach(async () => { + await resetDatabase(); +}); + +afterEach(async () => { + await resetDatabase(); +}); + +describe('attivita endpoints', () => { + test('GET /health risponde 200 con { status: "ok" }', async () => { + const res = await request(app).get('/health'); + + expect(res.status).toBe(200); + expect(res.body).toEqual({ status: 'ok' }); + }); + + test('GET /public/attivita/get/lista/home su DB vuoto restituisce []', async () => { + const res = await request(app).get('/public/attivita/get/lista/home'); + + expect(res.status).toBe(200); + expect(res.body).toEqual([]); + }); + + test('GET /public/attivita/get/one/:id con id non numerico restituisce 400', async () => { + const res = await request(app).get('/public/attivita/get/one/abc'); + + expect(res.status).toBe(400); + }); + + test('GET /public/attivita/get/one/:id con id inesistente restituisce 404', async () => { + const res = await request(app).get('/public/attivita/get/one/999999'); + + expect(res.status).toBe(404); + }); + + test('endpoint privati senza token restituiscono 401', async () => { + const saveRes = await request(app).post('/private/attivita/save').send(attivitaPayload()); + expect(saveRes.status).toBe(401); + + const myRes = await request(app).get('/private/attivita/get/lista/my'); + expect(myRes.status).toBe(401); + + const changeRes = await request(app).get('/private/attivita/change/stato/1/PU'); + expect(changeRes.status).toBe(401); + }); + + test('POST /private/attivita/save senza nome restituisce 400 con informazioni di validazione', async () => { + const { nome, ...payloadSenzaNome } = attivitaPayload(); + + const res = await request(app) + .post('/private/attivita/save') + .set('Authorization', AUTH_HEADER) + .send(payloadSenzaNome); + + expect(res.status).toBe(400); + expect(typeof res.body.message).toBe('string'); + expect(res.body.message.length).toBeGreaterThan(0); + expect(res.body.message).toContain('nome'); + }); + + test("POST /private/attivita/save con payload valido crea l'attività, visibile poi via get/lista/my", async () => { + const saveRes = await request(app) + .post('/private/attivita/save') + .set('Authorization', AUTH_HEADER) + .send(attivitaPayload()); + expect([200, 204]).toContain(saveRes.status); + + const myRes = await request(app) + .get('/private/attivita/get/lista/my') + .set('Authorization', AUTH_HEADER); + expect(myRes.status).toBe(200); + expect(myRes.body).toHaveLength(1); + expect(myRes.body[0]).toMatchObject({ nome: 'Caccia al tesoro', autore: 'Movio' }); + expect(myRes.body[0].brancaList.map((b: { nome: string }) => b.nome)).toEqual(['E/G']); + expect(myRes.body[0].categoriaList.map((c: { nome: string }) => c.nome)).toEqual([ + 'grande gioco', + ]); + }); + + test('get/lista/my non restituisce le attività di un altro autore', async () => { + await request(app).post('/private/attivita/save').set('Authorization', AUTH_HEADER).send(attivitaPayload()); + + const myRes = await request(app) + .get('/private/attivita/get/lista/my') + .set('Authorization', ALTRO_AUTH_HEADER); + + expect(myRes.status).toBe(200); + expect(myRes.body).toEqual([]); + }); + + test("POST /private/attivita/save su un'attività di un altro autore restituisce 403", async () => { + await request(app).post('/private/attivita/save').set('Authorization', AUTH_HEADER).send(attivitaPayload()); + const created = await prisma.attivita.findFirstOrThrow(); + + const res = await request(app) + .post('/private/attivita/save') + .set('Authorization', ALTRO_AUTH_HEADER) + .send(attivitaPayload({ id: created.id, nome: 'Modificata da altri' })); + + expect(res.status).toBe(403); + }); + + test("change/stato aggiorna lo stato e rende visibile l'attività in home", async () => { + await request(app).post('/private/attivita/save').set('Authorization', AUTH_HEADER).send(attivitaPayload()); + const created = await prisma.attivita.findFirstOrThrow(); + + const changeRes = await request(app) + .get(`/private/attivita/change/stato/${created.id}/PU`) + .set('Authorization', AUTH_HEADER); + expect(changeRes.status).toBe(200); + expect(changeRes.body).toEqual({ id: 'PU', nome: 'Pubblicato' }); + + const homeRes = await request(app).get('/public/attivita/get/lista/home'); + expect(homeRes.status).toBe(200); + expect(homeRes.body.map((a: { id: number }) => a.id)).toContain(created.id); + }); + + test('change/stato su id inesistente restituisce 404', async () => { + const res = await request(app) + .get('/private/attivita/change/stato/999999/PU') + .set('Authorization', AUTH_HEADER); + + expect(res.status).toBe(404); + }); + + test("change/stato su un'attività di un altro autore restituisce 403", async () => { + await request(app).post('/private/attivita/save').set('Authorization', AUTH_HEADER).send(attivitaPayload()); + const created = await prisma.attivita.findFirstOrThrow(); + + const res = await request(app) + .get(`/private/attivita/change/stato/${created.id}/PU`) + .set('Authorization', ALTRO_AUTH_HEADER); + + expect(res.status).toBe(403); + }); + + test('CORS: risponde con Access-Control-Allow-Origin per un Origin autorizzato', async () => { + const res = await request(app).get('/health').set('Origin', 'http://localhost:4200'); + + expect(res.headers['access-control-allow-origin']).toBe('http://localhost:4200'); + }); + + test('POST /public/attivita/get/lista/search con [] restituisce tutte le attività presenti', async () => { + await request(app) + .post('/private/attivita/save') + .set('Authorization', AUTH_HEADER) + .send(attivitaPayload({ nome: 'Prima' })); + await request(app) + .post('/private/attivita/save') + .set('Authorization', AUTH_HEADER) + .send(attivitaPayload({ nome: 'Seconda', stato: { id: 'PU', nome: 'Pubblicato' } })); + + const res = await request(app).post('/public/attivita/get/lista/search').send([]); + + expect(res.status).toBe(200); + expect(res.body).toHaveLength(2); + expect(res.body.map((a: { nome: string }) => a.nome).sort()).toEqual(['Prima', 'Seconda']); + }); +}); diff --git a/scouthub-attivita-be/tests/integration/autocomplete.endpoints.test.ts b/scouthub-attivita-be/tests/integration/autocomplete.endpoints.test.ts new file mode 100644 index 0000000..650470c --- /dev/null +++ b/scouthub-attivita-be/tests/integration/autocomplete.endpoints.test.ts @@ -0,0 +1,52 @@ +import request from 'supertest'; +import { app } from '../../src/app'; +import { prisma } from '../../src/db/prisma'; + +// L'unica tabella che questi endpoint (read-only sulle anagrafiche) potrebbero +// trovare "sporca" e' `materiale` (il seed non ne inserisce nessuno): la +// puliamo prima di ogni test per non dipendere dall'ordine di esecuzione +// rispetto alle altre suite. +beforeEach(async () => { + await prisma.materiale.deleteMany(); +}); + +describe('autocomplete endpoints', () => { + test('POST /public/autocomplete/get/search senza body contiene il gruppo Testo', async () => { + const res = await request(app).post('/public/autocomplete/get/search'); + + expect(res.status).toBe(200); + expect(res.body.some((g: { label: string }) => g.label === 'Testo')).toBe(true); + }); + + test('POST /public/autocomplete/get/search con "gioco" contiene Categoria con le sotto-categorie', async () => { + const res = await request(app) + .post('/public/autocomplete/get/search') + .set('Content-Type', 'application/json') + .send('"gioco"'); + + expect(res.status).toBe(200); + const categoria = res.body.find((g: { label: string }) => g.label === 'Categoria'); + expect(categoria).toBeDefined(); + expect(categoria.objectsList.map((o: { nome: string }) => o.nome).sort()).toEqual( + ["gioco", "gioco d'acqua", 'gioco giungla', 'gioco notturno', 'grande gioco'].sort(), + ); + }); + + test('POST /public/autocomplete/get/branca con "e/g" restituisce un solo elemento E/G', async () => { + const res = await request(app) + .post('/public/autocomplete/get/branca') + .set('Content-Type', 'application/json') + .send('"e/g"'); + + expect(res.status).toBe(200); + expect(res.body).toHaveLength(1); + expect(res.body[0]).toMatchObject({ nome: 'E/G', gruppo: 'branca' }); + }); + + test('POST /public/autocomplete/get/materiale senza body restituisce array vuoto', async () => { + const res = await request(app).post('/public/autocomplete/get/materiale'); + + expect(res.status).toBe(200); + expect(res.body).toEqual([]); + }); +}); diff --git a/scouthub-attivita-be/tests/setup/authTestHelper.ts b/scouthub-attivita-be/tests/setup/authTestHelper.ts new file mode 100644 index 0000000..13a4753 --- /dev/null +++ b/scouthub-attivita-be/tests/setup/authTestHelper.ts @@ -0,0 +1,41 @@ +import { generateKeyPairSync } from 'crypto'; +import jwt from 'jsonwebtoken'; +import nock from 'nock'; +import { env } from '../../src/config/env'; + +const KID = 'test-kid'; +const CERTS_PATH = `/realms/${env.keycloak.realm}/protocol/openid-connect/certs`; + +const { publicKey, privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }); +const jwk = publicKey.export({ format: 'jwk' }) as Record; +const privateKeyPem = privateKey.export({ type: 'pkcs1', format: 'pem' }) as string; + +const { privateKey: rogueKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }); +export const rogueKeyPem = rogueKey.export({ type: 'pkcs1', format: 'pem' }) as string; + +export function mockKeycloakJwks(): void { + nock(env.keycloak.baseUrl) + .persist() + .get(CERTS_PATH) + .reply(200, { keys: [{ ...jwk, kid: KID, alg: 'RS256', use: 'sig' }] }); +} + +interface TokenPayload { + sub: string; + email?: string; + name?: string; + realm_access?: { roles?: string[] }; +} + +export function signValidToken(payload: TokenPayload, signOptions: jwt.SignOptions = {}): string { + return jwt.sign(payload, privateKeyPem, { + algorithm: 'RS256', + keyid: KID, + expiresIn: '5m', + ...signOptions, + }); +} + +export function bearer(payload: TokenPayload, signOptions: jwt.SignOptions = {}): string { + return `Bearer ${signValidToken(payload, signOptions)}`; +} diff --git a/scouthub-attivita-be/tests/setup/globalSetup.ts b/scouthub-attivita-be/tests/setup/globalSetup.ts new file mode 100644 index 0000000..98bc07c --- /dev/null +++ b/scouthub-attivita-be/tests/setup/globalSetup.ts @@ -0,0 +1,26 @@ +import { execSync } from 'child_process'; +import dotenv from 'dotenv'; + +export default async function globalSetup(): Promise { + dotenv.config(); + + const databaseUrlTest = process.env.DATABASE_URL_TEST; + if (!databaseUrlTest) { + throw new Error("Variabile d'ambiente mancante: DATABASE_URL_TEST"); + } + + // Fa puntare tutto il codice applicativo (incluso src/db/prisma.ts) al DB di test, + // senza bisogno di modifiche altrove: dotenv.config() non sovrascrive DATABASE_URL + // gia' impostata qui. + process.env.DATABASE_URL = databaseUrlTest; + + execSync('npx prisma migrate deploy', { + env: process.env, + stdio: 'inherit', + }); + + execSync('npx prisma db seed', { + env: process.env, + stdio: 'inherit', + }); +} diff --git a/scouthub-attivita-be/tests/setup/globalTeardown.ts b/scouthub-attivita-be/tests/setup/globalTeardown.ts new file mode 100644 index 0000000..30c856b --- /dev/null +++ b/scouthub-attivita-be/tests/setup/globalTeardown.ts @@ -0,0 +1,5 @@ +import { prisma } from '../../src/db/prisma'; + +export default async function globalTeardown(): Promise { + await prisma.$disconnect(); +} diff --git a/scouthub-attivita-be/tests/setup/resetDatabase.ts b/scouthub-attivita-be/tests/setup/resetDatabase.ts new file mode 100644 index 0000000..42e7132 --- /dev/null +++ b/scouthub-attivita-be/tests/setup/resetDatabase.ts @@ -0,0 +1,18 @@ +import { prisma } from '../../src/db/prisma'; + +// Solo le tabelle "transazionali" popolate dai test: le anagrafiche di base +// seedate una volta sola (stato_attivita, tipo_categoria, tipo_paragrafo, +// branca, periodo_anno, categoria, materiale) restano intatte tra un test e l'altro. +const TABLES_TO_RESET = [ + 'paragrafo', + 'branca_attivita', + 'categoria_attivita', + 'materiale_attivita', + 'periodo_anno_attivita', + 'attivita', +]; + +export async function resetDatabase(): Promise { + const tables = TABLES_TO_RESET.map((table) => `"${table}"`).join(', '); + await prisma.$executeRawUnsafe(`TRUNCATE TABLE ${tables} RESTART IDENTITY CASCADE`); +} diff --git a/scouthub-attivita-be/tests/unit/attivita.service.test.ts b/scouthub-attivita-be/tests/unit/attivita.service.test.ts new file mode 100644 index 0000000..ccea5cf --- /dev/null +++ b/scouthub-attivita-be/tests/unit/attivita.service.test.ts @@ -0,0 +1,257 @@ +import { prisma } from '../../src/db/prisma'; +import { resetDatabase } from '../setup/resetDatabase'; +import { + changeStato, + getListaHome, + getListaSearch, + getListMy, + getOne, + save, +} from '../../src/modules/attivita/attivita.service'; +import { AuthContext } from '../../src/middlewares/auth.types'; +import { AttivitaSaveInput } from '../../src/types/validation'; + +// Questi test girano contro un DB Postgres di test reale (vedi tests/setup), +// non contro dei mock: il dominio ha query relazionali/JSON troppo specifiche +// per essere simulate in modo affidabile. + +const AUTH: AuthContext = { userId: 'user-test', email: 'test@example.com', name: 'Autore Test', roles: [] }; +const MARIO: AuthContext = { userId: 'user-mario', email: 'mario@example.com', name: 'Mario', roles: [] }; +const LUIGI: AuthContext = { userId: 'user-luigi', email: 'luigi@example.com', name: 'Luigi', roles: [] }; + +function baseAttivita(overrides: Partial = {}): AttivitaSaveInput { + return { + nome: 'Attività di test', + stato: { id: 'BO', nome: 'Bozza' }, + brancaList: [], + categoriaList: [], + materialeList: [], + periodoAnnoList: [], + paragrafoList: [], + ...overrides, + }; +} + +beforeEach(async () => { + await resetDatabase(); +}); + +afterEach(async () => { + await resetDatabase(); +}); + +describe('attivita.service', () => { + test('save collega una branca esistente per nome senza duplicarla', async () => { + await save(baseAttivita({ brancaList: [{ nome: 'E/G' }] }), AUTH); + + const brancheEG = await prisma.branca.findMany({ where: { nome: 'E/G' } }); + expect(brancheEG).toHaveLength(1); + + const attivita = await prisma.attivita.findFirstOrThrow(); + const link = await prisma.brancaAttivita.findUnique({ + where: { attivitaId_brancaId: { attivitaId: attivita.id, brancaId: brancheEG[0].id } }, + }); + expect(link).not.toBeNull(); + expect(link?.cancellato).toBe(false); + + const dto = await getOne(attivita.id); + expect(dto?.brancaList.map((b) => b.nome)).toEqual(['E/G']); + }); + + test('save crea branca/categoria/materiale/periodoAnno nuovi quando non esistono per nome', async () => { + await save( + baseAttivita({ + materialeList: [{ nome: 'Corda 10m', proprieta: { lunghezza: 10 } }], + }), + AUTH, + ); + + const materiale = await prisma.materiale.findFirst({ where: { nome: 'Corda 10m' } }); + expect(materiale).not.toBeNull(); + + const attivita = await prisma.attivita.findFirstOrThrow(); + const link = await prisma.materialeAttivita.findUnique({ + where: { attivitaId_materialeId: { attivitaId: attivita.id, materialeId: materiale!.id } }, + }); + expect(link).not.toBeNull(); + expect(link?.cancellato).toBe(false); + expect(link?.proprieta).toEqual({ lunghezza: 10 }); + }); + + test('save in aggiornamento marca come cancellato (soft-delete) il collegamento non più presente', async () => { + const lc = await prisma.branca.findFirstOrThrow({ where: { nome: 'L/C' } }); + const eg = await prisma.branca.findFirstOrThrow({ where: { nome: 'E/G' } }); + + await save(baseAttivita({ brancaList: [{ nome: 'L/C' }, { nome: 'E/G' }] }), AUTH); + const created = await prisma.attivita.findFirstOrThrow(); + + await save(baseAttivita({ id: created.id, brancaList: [{ nome: 'E/G' }] }), AUTH); + + const linkLc = await prisma.brancaAttivita.findUniqueOrThrow({ + where: { attivitaId_brancaId: { attivitaId: created.id, brancaId: lc.id } }, + }); + const linkEg = await prisma.brancaAttivita.findUniqueOrThrow({ + where: { attivitaId_brancaId: { attivitaId: created.id, brancaId: eg.id } }, + }); + + expect(linkLc.cancellato).toBe(true); + expect(linkEg.cancellato).toBe(false); + }); + + test('save sostituisce correttamente i paragrafi (modifica, rimozione, aggiunta)', async () => { + await save( + baseAttivita({ + paragrafoList: [ + { corpo: 'Paragrafo uno', tipo: { id: 'PARAGRAFO' }, ordine: 1 }, + { corpo: 'Paragrafo due', tipo: { id: 'PARAGRAFO' }, ordine: 2 }, + ], + }), + AUTH, + ); + + const created = await prisma.attivita.findFirstOrThrow(); + const paragrafi = await prisma.paragrafo.findMany({ + where: { attivitaId: created.id }, + orderBy: { ordine: 'asc' }, + }); + expect(paragrafi).toHaveLength(2); + const [primo, secondo] = paragrafi; + + await save( + baseAttivita({ + id: created.id, + paragrafoList: [ + { + id: primo.id, + corpo: 'Paragrafo uno modificato', + tipo: { id: 'PARAGRAFO' }, + ordine: 1, + }, + { corpo: 'Paragrafo nuovo', tipo: { id: 'PARAGRAFO' }, ordine: 2 }, + ], + }), + AUTH, + ); + + const paragrafiFinali = await prisma.paragrafo.findMany({ + where: { attivitaId: created.id }, + orderBy: { ordine: 'asc' }, + }); + + expect(paragrafiFinali).toHaveLength(2); + expect(paragrafiFinali.map((p) => p.corpo)).toEqual(['Paragrafo uno modificato', 'Paragrafo nuovo']); + expect(paragrafiFinali.find((p) => p.id === secondo.id)).toBeUndefined(); + }); + + test('save su id inesistente lancia HttpError 404', async () => { + await expect(save(baseAttivita({ id: 999999 }), AUTH)).rejects.toMatchObject({ statusCode: 404 }); + }); + + test("save su un'attività di un altro autore lancia HttpError 403", async () => { + await save(baseAttivita(), MARIO); + const created = await prisma.attivita.findFirstOrThrow(); + + await expect(save(baseAttivita({ id: created.id }), LUIGI)).rejects.toMatchObject({ statusCode: 403 }); + }); + + test('getListaHome restituisce solo attività pubblicate', async () => { + await save(baseAttivita({ nome: 'Bozza', stato: { id: 'BO', nome: 'Bozza' } }), AUTH); + await save(baseAttivita({ nome: 'Pubblicata', stato: { id: 'PU', nome: 'Pubblicato' } }), AUTH); + await save(baseAttivita({ nome: 'Privata', stato: { id: 'PR', nome: 'Privato' } }), AUTH); + + const home = await getListaHome(); + expect(home).toHaveLength(1); + expect(home[0].nome).toBe('Pubblicata'); + }); + + test('getListMy filtra per autore', async () => { + await save(baseAttivita({ nome: 'Di Mario' }), MARIO); + await save(baseAttivita({ nome: 'Di Luigi' }), LUIGI); + + const mie = await getListMy(MARIO.userId); + expect(mie).toHaveLength(1); + expect(mie[0].nome).toBe('Di Mario'); + expect(mie[0].autore).toBe('Mario'); + }); + + test('changeStato su id inesistente lancia HttpError 404', async () => { + await expect(changeStato(999999, 'PU', AUTH)).rejects.toMatchObject({ statusCode: 404 }); + }); + + test("changeStato su un'attività di un altro autore lancia HttpError 403", async () => { + await save(baseAttivita(), MARIO); + const created = await prisma.attivita.findFirstOrThrow(); + + await expect(changeStato(created.id, 'PU', LUIGI)).rejects.toMatchObject({ statusCode: 403 }); + }); + + test('changeStato aggiorna lo stato e lo restituisce come TipologicaDto', async () => { + await save(baseAttivita(), AUTH); + const created = await prisma.attivita.findFirstOrThrow(); + + const stato = await changeStato(created.id, 'PU', AUTH); + expect(stato).toEqual({ id: 'PU', nome: 'Pubblicato' }); + + const aggiornata = await prisma.attivita.findUniqueOrThrow({ where: { id: created.id } }); + expect(aggiornata.statoId).toBe('PU'); + }); + + test('getListaSearch combina filtri di gruppi diversi in AND e valori dello stesso gruppo in OR', async () => { + const lc = await prisma.branca.findFirstOrThrow({ where: { nome: 'L/C' } }); + const gioco = await prisma.categoria.findFirstOrThrow({ where: { nome: 'gioco' } }); + + await save( + baseAttivita({ + nome: 'L/C gioco', + brancaList: [{ nome: 'L/C' }], + categoriaList: [{ nome: 'gioco' }], + }), + AUTH, + ); + await save( + baseAttivita({ + nome: 'E/G gioco', + brancaList: [{ nome: 'E/G' }], + categoriaList: [{ nome: 'gioco' }], + }), + AUTH, + ); + await save( + baseAttivita({ + nome: 'L/C danza', + brancaList: [{ nome: 'L/C' }], + categoriaList: [{ nome: 'danza' }], + }), + AUTH, + ); + + const risultati = await getListaSearch([ + { gruppo: 'branca', id: lc.id, nome: null }, + { gruppo: 'categoria', id: gioco.id, nome: null }, + ]); + + expect(risultati).toHaveLength(1); + expect(risultati[0].nome).toBe('L/C gioco'); + }); + + test('getListaSearch con filtro testo trova sia nel nome sia nei paragrafi', async () => { + await save(baseAttivita({ nome: 'Caccia al tesoro' }), AUTH); + await save( + baseAttivita({ + nome: 'Attività generica', + paragrafoList: [ + { + corpo: "C'è un tesoro nascosto nel bosco", + tipo: { id: 'PARAGRAFO' }, + ordine: 1, + }, + ], + }), + AUTH, + ); + + const risultati = await getListaSearch([{ gruppo: 'testo', nome: 'tesoro', id: null }]); + + expect(risultati.map((r) => r.nome).sort()).toEqual(['Attività generica', 'Caccia al tesoro'].sort()); + }); +}); diff --git a/scouthub-attivita-be/tests/unit/autocomplete.service.test.ts b/scouthub-attivita-be/tests/unit/autocomplete.service.test.ts new file mode 100644 index 0000000..acc1165 --- /dev/null +++ b/scouthub-attivita-be/tests/unit/autocomplete.service.test.ts @@ -0,0 +1,89 @@ +import { prisma } from '../../src/db/prisma'; +import { + getBranca, + getCategoria, + getMateriale, + getPeriodoAnno, + getSearch, +} from '../../src/modules/autocomplete/autocomplete.service'; + +// Questo servizio legge solo le anagrafiche di base (branca, categoria, +// periodo_anno, materiale) gia' popolate dal seed: niente resetDatabase() +// completo qui, altrimenti perderemmo quei dati. L'unica tabella che questa +// suite potrebbe "sporcare" e' `materiale` (il seed non ne inserisce +// nessuno), quindi la puliamo prima di ogni test. +beforeEach(async () => { + await prisma.materiale.deleteMany(); +}); + +function nomi(list: { nome: string | null }[]): string[] { + return list.map((item) => item.nome ?? '').sort(); +} + +describe('autocomplete.service', () => { + test('getBranca è case-insensitive e restituisce il gruppo "branca"', async () => { + const risultati = await getBranca('e/g'); + + expect(risultati).toHaveLength(1); + expect(risultati[0]).toMatchObject({ nome: 'E/G', gruppo: 'branca' }); + }); + + test('getCategoria("gioco") trova la categoria e le sue sotto-categorie che contengono "gioco"', async () => { + const risultati = await getCategoria('gioco'); + + expect(nomi(risultati)).toEqual( + ['gioco', "gioco d'acqua", 'gioco giungla', 'gioco notturno', 'grande gioco'].sort(), + ); + expect(risultati.every((r) => r.gruppo === 'categoria')).toBe(true); + expect(nomi(risultati)).not.toContain('torneo'); + expect(nomi(risultati)).not.toContain('olimpiadi'); + }); + + test('getPeriodoAnno("campo") trova i periodi "campo" ma non "promessa"', async () => { + const risultati = await getPeriodoAnno('campo'); + + expect(nomi(risultati)).toEqual(['campo estivo', 'campo invernale'].sort()); + expect(risultati.every((r) => r.gruppo === 'periodoAnno')).toBe(true); + }); + + test('getMateriale su un database senza materiali restituisce array vuoto', async () => { + const risultati = await getMateriale('corda'); + + expect(risultati).toEqual([]); + }); + + test('getSearch(null) contiene sempre il gruppo Testo, popola Branca/Categoria/Periodo anno con tutte le righe e omette Materiale (vuoto)', async () => { + const gruppi = await getSearch(null); + + expect(gruppi[0]).toEqual({ + label: 'Testo', + objectsList: [{ id: null, nome: null, gruppo: 'testo' }], + }); + + const branca = gruppi.find((g) => g.label === 'Branca'); + const categoria = gruppi.find((g) => g.label === 'Categoria'); + const periodoAnno = gruppi.find((g) => g.label === 'Periodo anno'); + const materiale = gruppi.find((g) => g.label === 'Materiale'); + + expect(branca?.objectsList).toHaveLength(await prisma.branca.count()); + expect(categoria?.objectsList).toHaveLength(await prisma.categoria.count()); + expect(periodoAnno?.objectsList).toHaveLength(await prisma.periodoAnno.count()); + expect(materiale).toBeUndefined(); + }); + + test('getSearch("gioco") restituisce Testo e Categoria ma non Branca, Periodo anno o Materiale', async () => { + const gruppi = await getSearch('gioco'); + + const labels = gruppi.map((g) => g.label); + expect(labels).toContain('Testo'); + expect(labels).toContain('Categoria'); + expect(labels).not.toContain('Branca'); + expect(labels).not.toContain('Periodo anno'); + expect(labels).not.toContain('Materiale'); + + const categoria = gruppi.find((g) => g.label === 'Categoria'); + expect(nomi(categoria!.objectsList)).toEqual( + ['gioco', "gioco d'acqua", 'gioco giungla', 'gioco notturno', 'grande gioco'].sort(), + ); + }); +}); diff --git a/scouthub-attivita-be/tests/unit/middleware/authenticate.test.ts b/scouthub-attivita-be/tests/unit/middleware/authenticate.test.ts new file mode 100644 index 0000000..a3e1fcf --- /dev/null +++ b/scouthub-attivita-be/tests/unit/middleware/authenticate.test.ts @@ -0,0 +1,75 @@ +import express from 'express'; +import request from 'supertest'; +import jwt from 'jsonwebtoken'; +import nock from 'nock'; +import { authenticate } from '../../../src/middlewares/authenticate'; +import { bearer, mockKeycloakJwks, rogueKeyPem, signValidToken } from '../../setup/authTestHelper'; + +const CAPO_PAYLOAD = { + sub: 'user-123', + email: 'capo@example.com', + name: 'Capo Unità', + realm_access: { roles: ['capo-unita'] }, +}; + +function buildApp() { + const app = express(); + app.get('/protetta', authenticate, (req, res) => { + res.json({ auth: req.auth }); + }); + return app; +} + +beforeAll(() => { + mockKeycloakJwks(); +}); + +afterAll(() => { + nock.cleanAll(); +}); + +describe('authenticate', () => { + test('restituisce 401 se manca il token', async () => { + const app = buildApp(); + + const response = await request(app).get('/protetta'); + + expect(response.status).toBe(401); + }); + + test('restituisce 401 se il token è scaduto', async () => { + const app = buildApp(); + const token = signValidToken(CAPO_PAYLOAD, { expiresIn: '-10s' }); + + const response = await request(app).get('/protetta').set('Authorization', `Bearer ${token}`); + + expect(response.status).toBe(401); + }); + + test('restituisce 401 se il token ha una firma non valida', async () => { + const app = buildApp(); + const token = jwt.sign(CAPO_PAYLOAD, rogueKeyPem, { + algorithm: 'RS256', + keyid: 'test-kid', + expiresIn: '5m', + }); + + const response = await request(app).get('/protetta').set('Authorization', `Bearer ${token}`); + + expect(response.status).toBe(401); + }); + + test('restituisce 200 e popola req.auth se il token è valido', async () => { + const app = buildApp(); + + const response = await request(app).get('/protetta').set('Authorization', bearer(CAPO_PAYLOAD)); + + expect(response.status).toBe(200); + expect(response.body.auth).toEqual({ + userId: 'user-123', + email: 'capo@example.com', + name: 'Capo Unità', + roles: ['capo-unita'], + }); + }); +}); diff --git a/scouthub-attivita-be/tsconfig.jest.json b/scouthub-attivita-be/tsconfig.jest.json new file mode 100644 index 0000000..45223ea --- /dev/null +++ b/scouthub-attivita-be/tsconfig.jest.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true, + "declaration": false + }, + "include": ["src/**/*.ts", "tests/**/*.ts"] +} diff --git a/scouthub-attivita-be/tsconfig.json b/scouthub-attivita-be/tsconfig.json new file mode 100644 index 0000000..a6b633c --- /dev/null +++ b/scouthub-attivita-be/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2021", + "module": "CommonJS", + "lib": ["ES2021"], + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "moduleResolution": "node", + "declaration": false, + "sourceMap": true + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist", "tests"] +}