Compare commits
8
Commits
2e4b91f89b
...
72b7e25f86
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72b7e25f86 | ||
|
|
703c29bc83 | ||
|
|
b1ac2e1331 | ||
|
|
de28f26e0e | ||
|
|
ca49c6b5a8 | ||
|
|
6549ae1959 | ||
|
|
4993b75608 | ||
|
|
ae1393d3e3 |
No files matched your search
@@ -1,4 +1,5 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
|
node_modules.prev/
|
||||||
.turbo/
|
.turbo/
|
||||||
.next/
|
.next/
|
||||||
.next.prev/
|
.next.prev/
|
||||||
|
|||||||
@@ -7,16 +7,7 @@
|
|||||||
"src/app/**/{error,not-found,loading,template,default,global-error}.{ts,tsx}",
|
"src/app/**/{error,not-found,loading,template,default,global-error}.{ts,tsx}",
|
||||||
"scripts/migrate-aes-cbc-to-gcm.ts"
|
"scripts/migrate-aes-cbc-to-gcm.ts"
|
||||||
],
|
],
|
||||||
"project": ["src/**/*.{ts,tsx}", "scripts/**/*.{ts,js}"],
|
"project": ["src/**/*.{ts,tsx,css}", "scripts/**/*.{ts,js}"],
|
||||||
"ignoreDependencies": [
|
"ignoreDependencies": ["@sentry/nextjs", "pino-pretty", "husky"],
|
||||||
"tailwindcss-animate",
|
|
||||||
"@tailwindcss/forms",
|
|
||||||
"@tailwindcss/typography",
|
|
||||||
"pino-pretty",
|
|
||||||
"tailwindcss",
|
|
||||||
"husky",
|
|
||||||
"@sentry/nextjs"
|
|
||||||
],
|
|
||||||
"ignore": ["src/actions/import-badges.ts"],
|
|
||||||
"ignoreBinaries": ["sendmail"]
|
"ignoreBinaries": ["sendmail"]
|
||||||
}
|
}
|
||||||
+1
-1
@@ -36,7 +36,7 @@ const securityHeaders = [
|
|||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
cacheComponents: true, // <-- Dit activeert Cache Components en lost de 'instant' build-fout op!
|
cacheComponents: true, // <-- Dit activeert Cache Components en lost de 'instant' build-fout op!
|
||||||
deploymentId: resolveDeploymentId(),
|
deploymentId: resolveDeploymentId(),
|
||||||
serverExternalPackages: ["mariadb", "lzma", "sharp", "pino", "pino-pretty"],
|
serverExternalPackages: ["lzma", "sharp", "pino", "pino-pretty"],
|
||||||
|
|
||||||
// Onderdruk de specifieke Turbopack waarschuwingen voor brede bestandspatronen in de lib-map
|
// Onderdruk de specifieke Turbopack waarschuwingen voor brede bestandspatronen in de lib-map
|
||||||
turbopack: {
|
turbopack: {
|
||||||
|
|||||||
+10
-11
@@ -31,6 +31,7 @@
|
|||||||
"@dnd-kit/sortable": "10.0.0",
|
"@dnd-kit/sortable": "10.0.0",
|
||||||
"@dnd-kit/utilities": "3.2.2",
|
"@dnd-kit/utilities": "3.2.2",
|
||||||
"@hookform/resolvers": "5.7.1",
|
"@hookform/resolvers": "5.7.1",
|
||||||
|
"@next/bundle-analyzer": "16.3.0",
|
||||||
"@tanstack/react-virtual": "3.14.9",
|
"@tanstack/react-virtual": "3.14.9",
|
||||||
"class-variance-authority": "0.7.1",
|
"class-variance-authority": "0.7.1",
|
||||||
"clsx": "2.1.1",
|
"clsx": "2.1.1",
|
||||||
@@ -43,8 +44,8 @@
|
|||||||
"jpeg-js": "0.4.4",
|
"jpeg-js": "0.4.4",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"jszip": "3.10.1",
|
"jszip": "3.10.1",
|
||||||
"lenis": "1.3.25",
|
"lenis": "1.3.26",
|
||||||
"lucide-react": "1.28.0",
|
"lucide-react": "1.30.0",
|
||||||
"lzma": "2.3.2",
|
"lzma": "2.3.2",
|
||||||
"motion": "12.43.0",
|
"motion": "12.43.0",
|
||||||
"music-metadata": "11.14.0",
|
"music-metadata": "11.14.0",
|
||||||
@@ -56,37 +57,35 @@
|
|||||||
"pino": "10.3.1",
|
"pino": "10.3.1",
|
||||||
"react": "19.2.8",
|
"react": "19.2.8",
|
||||||
"react-dom": "19.2.8",
|
"react-dom": "19.2.8",
|
||||||
"react-hook-form": "7.84.0",
|
"react-hook-form": "7.85.0",
|
||||||
"resend": "6.18.1",
|
"resend": "6.18.1",
|
||||||
"server-only": "0.0.1",
|
"server-only": "0.0.1",
|
||||||
"sharp": "0.35.3",
|
"sharp": "0.35.3",
|
||||||
"sonner": "2.0.7",
|
"sonner": "2.0.8",
|
||||||
"tailwind-merge": "3.6.0",
|
"tailwind-merge": "3.6.0",
|
||||||
"tailwindcss-animate": "1.0.7",
|
"tailwindcss-animate": "1.0.7",
|
||||||
"zod": "4.4.3"
|
"zod": "4.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.5.7",
|
"@biomejs/biome": "2.5.7",
|
||||||
"@next/bundle-analyzer": "16.3.0",
|
|
||||||
"@tailwindcss/forms": "^0.5.11",
|
"@tailwindcss/forms": "^0.5.11",
|
||||||
"@tailwindcss/postcss": "^4.3.3",
|
"@tailwindcss/postcss": "^4.3.3",
|
||||||
"@tailwindcss/typography": "^0.5.20",
|
"@tailwindcss/typography": "^0.5.20",
|
||||||
"@types/node": "^26.1.2",
|
"@types/node": "^26.2.0",
|
||||||
"@types/react": "19.2.18",
|
"@types/react": "19.2.18",
|
||||||
"@types/react-dom": "19.2.4",
|
"@types/react-dom": "19.2.4",
|
||||||
"@vitest/coverage-v8": "4.1.10",
|
"@vitest/coverage-v8": "4.1.10",
|
||||||
"babel-plugin-react-compiler": "^1.0.0",
|
|
||||||
"dotenv": "^17.4.2",
|
"dotenv": "^17.4.2",
|
||||||
"drizzle-kit": "^0.31.10",
|
"drizzle-kit": "^0.31.10",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"knip": "6.31.0",
|
"knip": "6.32.0",
|
||||||
"lint-staged": "^17.3.0",
|
"lint-staged": "^17.3.0",
|
||||||
"pino-pretty": "^13.1.3",
|
"pino-pretty": "^13.1.3",
|
||||||
"postcss": "^8.5.25",
|
"postcss": "^8.5.26",
|
||||||
"tailwindcss": "^4.3.3",
|
"tailwindcss": "^4.3.3",
|
||||||
"tsx": "^4.23.5",
|
"tsx": "^4.23.11",
|
||||||
"typescript": "^7.0.2",
|
"typescript": "^7.0.2",
|
||||||
"vite": "8.2.0",
|
"vite": "8.2.1",
|
||||||
"vitest": "4.1.10"
|
"vitest": "4.1.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Generated
+83
-86
@@ -36,7 +36,10 @@ importers:
|
|||||||
version: 3.2.2([email protected])
|
version: 3.2.2([email protected])
|
||||||
'@hookform/resolvers':
|
'@hookform/resolvers':
|
||||||
specifier: 5.7.1
|
specifier: 5.7.1
|
||||||
version: 5.7.1(@standard-schema/[email protected])([email protected]4.0([email protected]))([email protected])
|
version: 5.7.1(@standard-schema/[email protected])([email protected]5.0([email protected]))([email protected])
|
||||||
|
'@next/bundle-analyzer':
|
||||||
|
specifier: 16.3.0
|
||||||
|
version: 16.3.0
|
||||||
'@tanstack/react-virtual':
|
'@tanstack/react-virtual':
|
||||||
specifier: 3.14.9
|
specifier: 3.14.9
|
||||||
version: 3.14.9([email protected]([email protected]))([email protected])
|
version: 3.14.9([email protected]([email protected]))([email protected])
|
||||||
@@ -57,7 +60,7 @@ importers:
|
|||||||
version: 3.4.13
|
version: 3.4.13
|
||||||
drizzle-orm:
|
drizzle-orm:
|
||||||
specifier: 0.45.2
|
specifier: 0.45.2
|
||||||
version: 0.45.2([email protected](@types/node@26.1.2))
|
version: 0.45.2([email protected](@types/node@26.2.0))
|
||||||
hash-wasm:
|
hash-wasm:
|
||||||
specifier: 4.12.0
|
specifier: 4.12.0
|
||||||
version: 4.12.0
|
version: 4.12.0
|
||||||
@@ -74,11 +77,11 @@ importers:
|
|||||||
specifier: 3.10.1
|
specifier: 3.10.1
|
||||||
version: 3.10.1
|
version: 3.10.1
|
||||||
lenis:
|
lenis:
|
||||||
specifier: 1.3.25
|
specifier: 1.3.26
|
||||||
version: 1.3.25([email protected])
|
version: 1.3.26([email protected])
|
||||||
lucide-react:
|
lucide-react:
|
||||||
specifier: 1.28.0
|
specifier: 1.30.0
|
||||||
version: 1.28.0([email protected])
|
version: 1.30.0([email protected])
|
||||||
lzma:
|
lzma:
|
||||||
specifier: 2.3.2
|
specifier: 2.3.2
|
||||||
version: 2.3.2
|
version: 2.3.2
|
||||||
@@ -90,16 +93,16 @@ importers:
|
|||||||
version: 11.14.0([email protected])
|
version: 11.14.0([email protected])
|
||||||
mysql2:
|
mysql2:
|
||||||
specifier: 3.23.2
|
specifier: 3.23.2
|
||||||
version: 3.23.2(@types/node@26.1.2)
|
version: 3.23.2(@types/node@26.2.0)
|
||||||
next:
|
next:
|
||||||
specifier: 16.3.0
|
specifier: 16.3.0
|
||||||
version: 16.3.0(@types/node@26.1.2)([email protected])([email protected]([email protected]))([email protected])
|
version: 16.3.0(@types/node@26.2.0)([email protected])([email protected]([email protected]))([email protected])
|
||||||
next-auth:
|
next-auth:
|
||||||
specifier: 5.0.0-beta.32
|
specifier: 5.0.0-beta.32
|
||||||
version: 5.0.0-beta.32([email protected](@types/node@26.1.2)([email protected])([email protected]([email protected]))([email protected]))([email protected])
|
version: 5.0.0-beta.32([email protected](@types/node@26.2.0)([email protected])([email protected]([email protected]))([email protected]))([email protected])
|
||||||
next-intl:
|
next-intl:
|
||||||
specifier: 4.13.5
|
specifier: 4.13.5
|
||||||
version: 4.13.5([email protected](@types/node@26.1.2)([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected])
|
version: 4.13.5([email protected](@types/node@26.2.0)([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected])
|
||||||
otplib:
|
otplib:
|
||||||
specifier: 13.4.1
|
specifier: 13.4.1
|
||||||
version: 13.4.1
|
version: 13.4.1
|
||||||
@@ -113,8 +116,8 @@ importers:
|
|||||||
specifier: 19.2.8
|
specifier: 19.2.8
|
||||||
version: 19.2.8([email protected])
|
version: 19.2.8([email protected])
|
||||||
react-hook-form:
|
react-hook-form:
|
||||||
specifier: 7.84.0
|
specifier: 7.85.0
|
||||||
version: 7.84.0([email protected])
|
version: 7.85.0([email protected])
|
||||||
resend:
|
resend:
|
||||||
specifier: 6.18.1
|
specifier: 6.18.1
|
||||||
version: 6.18.1
|
version: 6.18.1
|
||||||
@@ -123,10 +126,10 @@ importers:
|
|||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
sharp:
|
sharp:
|
||||||
specifier: ^0.35.3
|
specifier: ^0.35.3
|
||||||
version: 0.35.3(@types/node@26.1.2)
|
version: 0.35.3(@types/node@26.2.0)
|
||||||
sonner:
|
sonner:
|
||||||
specifier: 2.0.7
|
specifier: 2.0.8
|
||||||
version: 2.0.7([email protected]([email protected]))([email protected])
|
version: 2.0.8(@types/[email protected])([email protected]([email protected]))([email protected])
|
||||||
tailwind-merge:
|
tailwind-merge:
|
||||||
specifier: 3.6.0
|
specifier: 3.6.0
|
||||||
version: 3.6.0
|
version: 3.6.0
|
||||||
@@ -140,9 +143,6 @@ importers:
|
|||||||
'@biomejs/biome':
|
'@biomejs/biome':
|
||||||
specifier: 2.5.7
|
specifier: 2.5.7
|
||||||
version: 2.5.7
|
version: 2.5.7
|
||||||
'@next/bundle-analyzer':
|
|
||||||
specifier: 16.3.0
|
|
||||||
version: 16.3.0
|
|
||||||
'@tailwindcss/forms':
|
'@tailwindcss/forms':
|
||||||
specifier: ^0.5.11
|
specifier: ^0.5.11
|
||||||
version: 0.5.11([email protected])
|
version: 0.5.11([email protected])
|
||||||
@@ -153,8 +153,8 @@ importers:
|
|||||||
specifier: ^0.5.20
|
specifier: ^0.5.20
|
||||||
version: 0.5.20([email protected])
|
version: 0.5.20([email protected])
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^26.1.2
|
specifier: ^26.2.0
|
||||||
version: 26.1.2
|
version: 26.2.0
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: 19.2.18
|
specifier: 19.2.18
|
||||||
version: 19.2.18
|
version: 19.2.18
|
||||||
@@ -164,9 +164,6 @@ importers:
|
|||||||
'@vitest/coverage-v8':
|
'@vitest/coverage-v8':
|
||||||
specifier: 4.1.10
|
specifier: 4.1.10
|
||||||
version: 4.1.10([email protected])
|
version: 4.1.10([email protected])
|
||||||
babel-plugin-react-compiler:
|
|
||||||
specifier: ^1.0.0
|
|
||||||
version: 1.0.0
|
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^17.4.2
|
specifier: ^17.4.2
|
||||||
version: 17.4.2
|
version: 17.4.2
|
||||||
@@ -177,8 +174,8 @@ importers:
|
|||||||
specifier: ^9.1.7
|
specifier: ^9.1.7
|
||||||
version: 9.1.7
|
version: 9.1.7
|
||||||
knip:
|
knip:
|
||||||
specifier: 6.31.0
|
specifier: 6.32.0
|
||||||
version: 6.31.0
|
version: 6.32.0
|
||||||
lint-staged:
|
lint-staged:
|
||||||
specifier: ^17.3.0
|
specifier: ^17.3.0
|
||||||
version: 17.3.0
|
version: 17.3.0
|
||||||
@@ -192,17 +189,17 @@ importers:
|
|||||||
specifier: ^4.3.3
|
specifier: ^4.3.3
|
||||||
version: 4.3.3
|
version: 4.3.3
|
||||||
tsx:
|
tsx:
|
||||||
specifier: ^4.23.5
|
specifier: ^4.23.11
|
||||||
version: 4.23.5
|
version: 4.23.11
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^7.0.2
|
specifier: ^7.0.2
|
||||||
version: 7.0.2
|
version: 7.0.2
|
||||||
vite:
|
vite:
|
||||||
specifier: 8.2.0
|
specifier: 8.2.1
|
||||||
version: 8.2.0(@types/node@26.1.2)([email protected])([email protected])([email protected].5)([email protected])
|
version: 8.2.1(@types/node@26.2.0)([email protected])([email protected])([email protected].11)([email protected])
|
||||||
vitest:
|
vitest:
|
||||||
specifier: 4.1.10
|
specifier: 4.1.10
|
||||||
version: 4.1.10(@types/node@26.1.2)(@vitest/[email protected])([email protected].0(@types/node@26.1.2)([email protected])([email protected])([email protected].5)([email protected]))
|
version: 4.1.10(@types/node@26.2.0)(@vitest/[email protected])([email protected].1(@types/node@26.2.0)([email protected])([email protected])([email protected].11)([email protected]))
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -1734,8 +1731,8 @@ packages:
|
|||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||||
|
|
||||||
'@types/node@26.1.2':
|
'@types/node@26.2.0':
|
||||||
resolution: {integrity: sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==}
|
resolution: {integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==}
|
resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==}
|
||||||
@@ -2215,9 +2212,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
|
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
[email protected]:
|
|
||||||
resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==}
|
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-Dz/6HxkrxgNehhxLVeyv8sad9UzF2xBVeaKBQNDfJ5XiSXmp2gTR0eO0RWiT2NCKS5aGP9jjkOMggTN90qU50A==}
|
resolution: {integrity: sha512-Dz/6HxkrxgNehhxLVeyv8sad9UzF2xBVeaKBQNDfJ5XiSXmp2gTR0eO0RWiT2NCKS5aGP9jjkOMggTN90qU50A==}
|
||||||
|
|
||||||
@@ -2324,13 +2318,13 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
||||||
|
|
||||||
[email protected]1.0:
|
[email protected]2.0:
|
||||||
resolution: {integrity: sha512-NbeIEmUS2VUMjAkbiSNOKPJeV9wpCsr0660sUyKyMQbk4Iom0++nTLInVp4MJ+LfR4kORnw67bDi5tvO7YLnzA==}
|
resolution: {integrity: sha512-KDX9OmmOFmlvmxTkrx6Z0GHISMut+pXMSKR8eg84bovaxJKx2NdQD4JYCXveSbvieRe107W6vCD2xCpmz0qBYA==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
[email protected]5:
|
[email protected]6:
|
||||||
resolution: {integrity: sha512-mOKxayErlaONK8fm4LN3XNd99Qu4plTpn9h9qf8wxzjGrJDzuD84FYzZ81HCd6ZsWp++VWVwOzL286Pf2s2u4A==}
|
resolution: {integrity: sha512-s/xTCZCxTFvHbAN1OzuhNaN5YPJH2ail0XAkctKW1b+RUAG4nUL5UHLXwNko1h8aEeT2jspBXegMgPJd8zcuag==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@nuxt/kit': '>=3.0.0'
|
'@nuxt/kit': '>=3.0.0'
|
||||||
react: '>=17.0.0'
|
react: '>=17.0.0'
|
||||||
@@ -2506,8 +2500,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==}
|
resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==}
|
||||||
engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'}
|
engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'}
|
||||||
|
|
||||||
lucide-react@1.28.0:
|
lucide-react@1.30.0:
|
||||||
resolution: {integrity: sha512-fARAFJULsGuDDydjp6+6blekG/sBIM29TerzLjc9bQUKAcEfrSc4ZQKb25KRz4OMKd87cZTb5dgq0w/T6KufVg==}
|
resolution: {integrity: sha512-tUIr2jXLbWpCkdtH8XP7P7YppM9ueWgTky99lpWDY6z5REs6B+O6ZQ3U5tHkUUY59ANyOv/PBcs8E4Fe3KO3eA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
@@ -2733,8 +2727,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^19.2.8
|
react: ^19.2.8
|
||||||
|
|
||||||
[email protected]4.0:
|
[email protected]5.0:
|
||||||
resolution: {integrity: sha512-+hWvQP6GLco56mDwrbU4XnHix8t1z90ltZsDIrREl+jnQFQxYLX8oAzqe/Xn8nHpmoXTY5M6oEXrAhbP1qevNQ==}
|
resolution: {integrity: sha512-U2MTriFXnclmV4rOE20p2DcRFv5WEg3FIcBFOKcOLFHDVvGIMPvLTkTWefUsonmlaVy23khVDxDWym6uJVGOzw==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.8.0 || ^17 || ^18 || ^19
|
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||||
@@ -2857,11 +2851,15 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==}
|
resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==}
|
||||||
|
|
||||||
[email protected].7:
|
[email protected].8:
|
||||||
resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
|
resolution: {integrity: sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
'@types/react': 19.2.18
|
||||||
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||||
@@ -2971,8 +2969,8 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
[email protected].5:
|
[email protected].11:
|
||||||
resolution: {integrity: sha512-rw55FUaqOoI7RvlQwLbhO4nSDApnQ4/CykPuiQ/EPvtrX3WA9Ig55jIt9VvbBJbzJuj12ueRu4PMZ2SxPVbihg==}
|
resolution: {integrity: sha512-Ry2oTEUnhBdeEdWIztY8kf3/nBGnPnjMLVGL0YfdRXMORuPER5NlKmayqxtxRxwB1xBN+RivRaJfe7PM1rtiyw==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -3025,8 +3023,8 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
|
||||||
[email protected].0:
|
[email protected].1:
|
||||||
resolution: {integrity: sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==}
|
resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3405,10 +3403,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@formatjs/fast-memoize': 3.1.7
|
'@formatjs/fast-memoize': 3.1.7
|
||||||
|
|
||||||
'@hookform/[email protected](@standard-schema/[email protected])([email protected]4.0([email protected]))([email protected])':
|
'@hookform/[email protected](@standard-schema/[email protected])([email protected]5.0([email protected]))([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@standard-schema/utils': 0.3.0
|
'@standard-schema/utils': 0.3.0
|
||||||
react-hook-form: 7.84.0([email protected])
|
react-hook-form: 7.85.0([email protected])
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@standard-schema/spec': 1.1.0
|
'@standard-schema/spec': 1.1.0
|
||||||
zod: 4.4.3
|
zod: 4.4.3
|
||||||
@@ -4163,7 +4161,7 @@ snapshots:
|
|||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/node@26.1.2':
|
'@types/node@26.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 8.3.0
|
undici-types: 8.3.0
|
||||||
|
|
||||||
@@ -4250,7 +4248,7 @@ snapshots:
|
|||||||
obug: 2.1.4
|
obug: 2.1.4
|
||||||
std-env: 4.2.0
|
std-env: 4.2.0
|
||||||
tinyrainbow: 3.1.1
|
tinyrainbow: 3.1.1
|
||||||
vitest: 4.1.10(@types/node@26.1.2)(@vitest/[email protected])([email protected].0(@types/node@26.1.2)([email protected])([email protected])([email protected].5)([email protected]))
|
vitest: 4.1.10(@types/node@26.2.0)(@vitest/[email protected])([email protected].1(@types/node@26.2.0)([email protected])([email protected])([email protected].11)([email protected]))
|
||||||
|
|
||||||
'@vitest/[email protected]':
|
'@vitest/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4261,13 +4259,13 @@ snapshots:
|
|||||||
chai: 6.2.2
|
chai: 6.2.2
|
||||||
tinyrainbow: 3.1.1
|
tinyrainbow: 3.1.1
|
||||||
|
|
||||||
'@vitest/[email protected]([email protected].0(@types/node@26.1.2)([email protected])([email protected])([email protected].5)([email protected]))':
|
'@vitest/[email protected]([email protected].1(@types/node@26.2.0)([email protected])([email protected])([email protected].11)([email protected]))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/spy': 4.1.10
|
'@vitest/spy': 4.1.10
|
||||||
estree-walker: 3.0.3
|
estree-walker: 3.0.3
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 8.2.0(@types/node@26.1.2)([email protected])([email protected])([email protected].5)([email protected])
|
vite: 8.2.1(@types/node@26.2.0)([email protected])([email protected])([email protected].11)([email protected])
|
||||||
|
|
||||||
'@vitest/[email protected]':
|
'@vitest/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4318,6 +4316,7 @@ snapshots:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.29.8
|
'@babel/types': 7.29.8
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -4392,11 +4391,11 @@ snapshots:
|
|||||||
'@drizzle-team/brocli': 0.10.2
|
'@drizzle-team/brocli': 0.10.2
|
||||||
'@esbuild-kit/esm-loader': 2.6.5
|
'@esbuild-kit/esm-loader': 2.6.5
|
||||||
esbuild: 0.25.12
|
esbuild: 0.25.12
|
||||||
tsx: 4.23.5
|
tsx: 4.23.11
|
||||||
|
|
||||||
[email protected]([email protected](@types/node@26.1.2)):
|
[email protected]([email protected](@types/node@26.2.0)):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
mysql2: 3.23.2(@types/node@26.1.2)
|
mysql2: 3.23.2(@types/node@26.2.0)
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -4493,10 +4492,6 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
|
||||||
dependencies:
|
|
||||||
resolve-pkg-maps: 1.0.0
|
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
resolve-pkg-maps: 1.0.0
|
resolve-pkg-maps: 1.0.0
|
||||||
@@ -4591,11 +4586,11 @@ snapshots:
|
|||||||
readable-stream: 2.3.8
|
readable-stream: 2.3.8
|
||||||
setimmediate: 1.0.5
|
setimmediate: 1.0.5
|
||||||
|
|
||||||
[email protected]1.0:
|
[email protected]2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
fdir: 6.5.0([email protected])
|
fdir: 6.5.0([email protected])
|
||||||
formatly: 0.3.0
|
formatly: 0.3.0
|
||||||
get-tsconfig: 4.14.0
|
get-tsconfig: 4.14.1
|
||||||
jiti: 2.7.0
|
jiti: 2.7.0
|
||||||
oxc-parser: 0.142.0
|
oxc-parser: 0.142.0
|
||||||
oxc-resolver: 11.24.2
|
oxc-resolver: 11.24.2
|
||||||
@@ -4607,7 +4602,7 @@ snapshots:
|
|||||||
yaml: 2.9.0
|
yaml: 2.9.0
|
||||||
zod: 4.4.3
|
zod: 4.4.3
|
||||||
|
|
||||||
[email protected]5([email protected]):
|
[email protected]6([email protected]):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
|
|
||||||
@@ -4725,7 +4720,7 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
lucide-react@1.28.0([email protected]):
|
lucide-react@1.30.0([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
|
|
||||||
@@ -4784,9 +4779,9 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
[email protected](@types/node@26.1.2):
|
[email protected](@types/node@26.2.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 26.1.2
|
'@types/node': 26.2.0
|
||||||
aws-ssl-profiles: 1.1.2
|
aws-ssl-profiles: 1.1.2
|
||||||
denque: 2.1.0
|
denque: 2.1.0
|
||||||
generate-function: 2.3.1
|
generate-function: 2.3.1
|
||||||
@@ -4804,22 +4799,22 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]([email protected](@types/node@26.1.2)([email protected])([email protected]([email protected]))([email protected]))([email protected]):
|
[email protected]([email protected](@types/node@26.2.0)([email protected])([email protected]([email protected]))([email protected]))([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@auth/core': 0.41.3
|
'@auth/core': 0.41.3
|
||||||
next: 16.3.0(@types/node@26.1.2)([email protected])([email protected]([email protected]))([email protected])
|
next: 16.3.0(@types/node@26.2.0)([email protected])([email protected]([email protected]))([email protected])
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]([email protected](@types/node@26.1.2)([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected]):
|
[email protected]([email protected](@types/node@26.2.0)([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formatjs/intl-localematcher': 0.8.13
|
'@formatjs/intl-localematcher': 0.8.13
|
||||||
'@parcel/watcher': 2.6.0
|
'@parcel/watcher': 2.6.0
|
||||||
'@swc/core': 1.15.47
|
'@swc/core': 1.15.47
|
||||||
icu-minify: 4.13.5
|
icu-minify: 4.13.5
|
||||||
negotiator: 1.0.0
|
negotiator: 1.0.0
|
||||||
next: 16.3.0(@types/node@26.1.2)([email protected])([email protected]([email protected]))([email protected])
|
next: 16.3.0(@types/node@26.2.0)([email protected])([email protected]([email protected]))([email protected])
|
||||||
next-intl-swc-plugin-extractor: 4.13.5
|
next-intl-swc-plugin-extractor: 4.13.5
|
||||||
po-parser: 2.1.1
|
po-parser: 2.1.1
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
@@ -4829,7 +4824,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@swc/helpers'
|
- '@swc/helpers'
|
||||||
|
|
||||||
[email protected](@types/node@26.1.2)([email protected])([email protected]([email protected]))([email protected]):
|
[email protected](@types/node@26.2.0)([email protected])([email protected]([email protected]))([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 16.3.0
|
'@next/env': 16.3.0
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
@@ -4849,7 +4844,7 @@ snapshots:
|
|||||||
'@next/swc-win32-arm64-msvc': 16.3.0
|
'@next/swc-win32-arm64-msvc': 16.3.0
|
||||||
'@next/swc-win32-x64-msvc': 16.3.0
|
'@next/swc-win32-x64-msvc': 16.3.0
|
||||||
babel-plugin-react-compiler: 1.0.0
|
babel-plugin-react-compiler: 1.0.0
|
||||||
sharp: 0.35.3(@types/node@26.1.2)
|
sharp: 0.35.3(@types/node@26.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
@@ -5006,7 +5001,7 @@ snapshots:
|
|||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
scheduler: 0.27.0
|
scheduler: 0.27.0
|
||||||
|
|
||||||
[email protected]4.0([email protected]):
|
[email protected]5.0([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
|
|
||||||
@@ -5100,7 +5095,7 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected](@types/node@26.1.2):
|
[email protected](@types/node@26.2.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@img/colour': 1.1.0
|
'@img/colour': 1.1.0
|
||||||
detect-libc: 2.1.2
|
detect-libc: 2.1.2
|
||||||
@@ -5131,7 +5126,7 @@ snapshots:
|
|||||||
'@img/sharp-win32-arm64': 0.35.3
|
'@img/sharp-win32-arm64': 0.35.3
|
||||||
'@img/sharp-win32-ia32': 0.35.3
|
'@img/sharp-win32-ia32': 0.35.3
|
||||||
'@img/sharp-win32-x64': 0.35.3
|
'@img/sharp-win32-x64': 0.35.3
|
||||||
'@types/node': 26.1.2
|
'@types/node': 26.2.0
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -5147,10 +5142,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
atomic-sleep: 1.0.0
|
atomic-sleep: 1.0.0
|
||||||
|
|
||||||
[email protected].7([email protected]([email protected]))([email protected]):
|
[email protected].8(@types/[email protected])([email protected]([email protected]))([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
react-dom: 19.2.8([email protected])
|
react-dom: 19.2.8([email protected])
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.2.18
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -5232,7 +5229,7 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected].5:
|
[email protected].11:
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.25.12
|
esbuild: 0.25.12
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -5296,7 +5293,7 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected].0(@types/node@26.1.2)([email protected])([email protected])([email protected].5)([email protected]):
|
[email protected].1(@types/node@26.2.0)([email protected])([email protected])([email protected].11)([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
lightningcss: 1.33.0
|
lightningcss: 1.33.0
|
||||||
picomatch: 4.0.5
|
picomatch: 4.0.5
|
||||||
@@ -5304,17 +5301,17 @@ snapshots:
|
|||||||
rolldown: 1.2.2
|
rolldown: 1.2.2
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 26.1.2
|
'@types/node': 26.2.0
|
||||||
esbuild: 0.25.12
|
esbuild: 0.25.12
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
jiti: 2.7.0
|
jiti: 2.7.0
|
||||||
tsx: 4.23.5
|
tsx: 4.23.11
|
||||||
yaml: 2.9.0
|
yaml: 2.9.0
|
||||||
|
|
||||||
[email protected](@types/node@26.1.2)(@vitest/[email protected])([email protected].0(@types/node@26.1.2)([email protected])([email protected])([email protected].5)([email protected])):
|
[email protected](@types/node@26.2.0)(@vitest/[email protected])([email protected].1(@types/node@26.2.0)([email protected])([email protected])([email protected].11)([email protected])):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 4.1.10
|
'@vitest/expect': 4.1.10
|
||||||
'@vitest/mocker': 4.1.10([email protected].0(@types/node@26.1.2)([email protected])([email protected])([email protected].5)([email protected]))
|
'@vitest/mocker': 4.1.10([email protected].1(@types/node@26.2.0)([email protected])([email protected])([email protected].11)([email protected]))
|
||||||
'@vitest/pretty-format': 4.1.10
|
'@vitest/pretty-format': 4.1.10
|
||||||
'@vitest/runner': 4.1.10
|
'@vitest/runner': 4.1.10
|
||||||
'@vitest/snapshot': 4.1.10
|
'@vitest/snapshot': 4.1.10
|
||||||
@@ -5331,10 +5328,10 @@ snapshots:
|
|||||||
tinyexec: 1.3.0
|
tinyexec: 1.3.0
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
tinyrainbow: 3.1.1
|
tinyrainbow: 3.1.1
|
||||||
vite: 8.2.0(@types/node@26.1.2)([email protected])([email protected])([email protected].5)([email protected])
|
vite: 8.2.1(@types/node@26.2.0)([email protected])([email protected])([email protected].11)([email protected])
|
||||||
why-is-node-running: 2.3.0
|
why-is-node-running: 2.3.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 26.1.2
|
'@types/node': 26.2.0
|
||||||
'@vitest/coverage-v8': 4.1.10([email protected])
|
'@vitest/coverage-v8': 4.1.10([email protected])
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- msw
|
- msw
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
"use server";
|
|
||||||
|
|
||||||
import { requirePermission } from "@/lib/admin/guard";
|
|
||||||
import { PERMS } from "@/lib/permissions";
|
|
||||||
import { importBadgeSynced } from "@/lib/services/import-badge";
|
|
||||||
|
|
||||||
export async function importBadgeFromRemote({
|
|
||||||
code,
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
}: {
|
|
||||||
code: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
}) {
|
|
||||||
await requirePermission(PERMS.ASSETS_IMPORT);
|
|
||||||
const result = await importBadgeSynced({ code, name, description });
|
|
||||||
if (!result.ok) {
|
|
||||||
return { ok: false as const, error: result.error };
|
|
||||||
}
|
|
||||||
return { ok: true as const };
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
import { Languages, Package, Settings } from "lucide-react";
|
import { Languages, Package, Settings } from "lucide-react";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import type { CatalogItemData } from "./catalog-items-table";
|
import { type CatalogItemData, CatalogItemsTable } from "./catalog-items-table";
|
||||||
import { CatalogItemsTable } from "./catalog-items-table";
|
|
||||||
import { CatalogPageForm } from "./catalog-page-form";
|
import { CatalogPageForm } from "./catalog-page-form";
|
||||||
import { CatalogTranslateTab } from "./catalog-translate-tab";
|
import { CatalogTranslateTab } from "./catalog-translate-tab";
|
||||||
|
|
||||||
|
|||||||
@@ -90,8 +90,14 @@ import {
|
|||||||
} from "./field-helpers";
|
} from "./field-helpers";
|
||||||
import { FurniIcon } from "./furni-icon";
|
import { FurniIcon } from "./furni-icon";
|
||||||
import { SongPicker } from "./song-picker";
|
import { SongPicker } from "./song-picker";
|
||||||
import type { BaseItemData, CatalogItemData, InlineEdit } from "./types";
|
import {
|
||||||
import { DEFAULT_NEW_ITEM, getPointsLabel, POINTS_TYPES } from "./types";
|
type BaseItemData,
|
||||||
|
type CatalogItemData,
|
||||||
|
DEFAULT_NEW_ITEM,
|
||||||
|
getPointsLabel,
|
||||||
|
type InlineEdit,
|
||||||
|
POINTS_TYPES,
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
export type { BaseItemData, CatalogItemData };
|
export type { BaseItemData, CatalogItemData };
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import {
|
|||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import {
|
import {
|
||||||
getCachedSongPickerOptions,
|
getCachedSongPickerOptions,
|
||||||
|
invalidateSongPickerCache,
|
||||||
loadSongPickerOptions,
|
loadSongPickerOptions,
|
||||||
type SoundtrackOption,
|
type SoundtrackOption,
|
||||||
} from "@/lib/client-cache/song-picker-cache";
|
} from "@/lib/client-cache/song-picker-cache";
|
||||||
|
|
||||||
// Re-export so existing `import { invalidateSongPickerCache } from '.../song-picker'`
|
// Re-export so existing `import { invalidateSongPickerCache } from '.../song-picker'`
|
||||||
// callsites keep working. New code should import from the lib module directly.
|
// callsites keep working. New code should import from the lib module directly.
|
||||||
export { invalidateSongPickerCache } from "@/lib/client-cache/song-picker-cache";
|
export { invalidateSongPickerCache };
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value: number;
|
value: number;
|
||||||
|
|||||||
@@ -39,30 +39,56 @@ interface NitroEditorDialogProps {
|
|||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep these helpers on `any` — Next's build-time checker has repeatedly
|
interface NitroVisualization {
|
||||||
// disagreed with `tsc` on stricter JSON typings for this editor.
|
size?: number;
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: metadata tree is untyped JSON
|
layers?: Record<string, { z?: number; alpha?: number; ink?: string }>;
|
||||||
function updateNested(obj: any, path: string, value: unknown): any {
|
colors?: Record<string, Record<string, { color: number }>>;
|
||||||
const clone = JSON.parse(JSON.stringify(obj));
|
animations?: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The .nitro metadata is untyped JSON from disk; typed only for the fields the
|
||||||
|
// editor reads/writes. Unknown keys are traversed via the path helpers below.
|
||||||
|
interface NitroMetadata {
|
||||||
|
visualizationType?: string;
|
||||||
|
logicType?: string;
|
||||||
|
logic?: {
|
||||||
|
model?: {
|
||||||
|
dimensions?: { x: number; y: number; z: number };
|
||||||
|
directions?: number[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
visualizations?: NitroVisualization[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonObject = Record<string, unknown>;
|
||||||
|
|
||||||
|
function updateNested<T extends object>(
|
||||||
|
obj: T,
|
||||||
|
path: string,
|
||||||
|
value: unknown,
|
||||||
|
): T {
|
||||||
|
const clone: T = JSON.parse(JSON.stringify(obj));
|
||||||
const keys = path.split(".");
|
const keys = path.split(".");
|
||||||
let current = clone;
|
let current: JsonObject = clone as JsonObject;
|
||||||
for (let i = 0; i < keys.length - 1; i++) {
|
for (let i = 0; i < keys.length - 1; i++) {
|
||||||
if (!current[keys[i]]) current[keys[i]] = {};
|
const next = current[keys[i]];
|
||||||
current = current[keys[i]];
|
if (!next || typeof next !== "object" || Array.isArray(next)) {
|
||||||
|
current[keys[i]] = {};
|
||||||
|
}
|
||||||
|
current = current[keys[i]] as JsonObject;
|
||||||
}
|
}
|
||||||
current[keys[keys.length - 1]] = value;
|
current[keys[keys.length - 1]] = value;
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: metadata tree is untyped JSON
|
function getNested<T>(obj: unknown, path: string, fallback: T): T {
|
||||||
function getNested(obj: any, path: string, fallback?: unknown): any {
|
|
||||||
const keys = path.split(".");
|
const keys = path.split(".");
|
||||||
let current = obj;
|
let current: unknown = obj;
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
if (current == null || typeof current !== "object") return fallback;
|
if (current == null || typeof current !== "object") return fallback;
|
||||||
current = current[key];
|
current = (current as JsonObject)[key];
|
||||||
}
|
}
|
||||||
return current ?? fallback;
|
return current === undefined ? fallback : (current as T);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALL_DIRECTIONS = [0, 1, 2, 3, 4, 5, 6, 7];
|
const ALL_DIRECTIONS = [0, 1, 2, 3, 4, 5, 6, 7];
|
||||||
@@ -99,10 +125,9 @@ export function NitroEditorDialog({
|
|||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: NitroEditorDialogProps) {
|
}: NitroEditorDialogProps) {
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: explicitly chosen here, see surrounding code
|
const [metadata, setMetadata] = useState<NitroMetadata | null>(null);
|
||||||
const [metadata, setMetadata] = useState<any>(null);
|
const [originalMetadata, setOriginalMetadata] =
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: explicitly chosen here, see surrounding code
|
useState<NitroMetadata | null>(null);
|
||||||
const [originalMetadata, setOriginalMetadata] = useState<any>(null);
|
|
||||||
const [jsonText, setJsonText] = useState("");
|
const [jsonText, setJsonText] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
@@ -120,10 +145,9 @@ export function NitroEditorDialog({
|
|||||||
const [jsonError, setJsonError] = useState<string | null>(null);
|
const [jsonError, setJsonError] = useState<string | null>(null);
|
||||||
|
|
||||||
const patchMeta = (path: string, value: unknown) => {
|
const patchMeta = (path: string, value: unknown) => {
|
||||||
// Explicit any avoids noImplicitAny on updater params and dodges
|
setMetadata((prev) =>
|
||||||
// Next build's stricter JSON inference that disagreed with tsc.
|
prev == null ? prev : updateNested(prev, path, value),
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: metadata tree is untyped JSON
|
);
|
||||||
setMetadata((prev: any) => updateNested(prev, path, value));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchMetadata = useCallback(async () => {
|
const fetchMetadata = useCallback(async () => {
|
||||||
@@ -229,14 +253,12 @@ export function NitroEditorDialog({
|
|||||||
|
|
||||||
// ── Visual tab helpers ──
|
// ── Visual tab helpers ──
|
||||||
|
|
||||||
const dims = (
|
const dims = metadata
|
||||||
metadata
|
? getNested(metadata, "logic.model.dimensions", { x: 1, y: 1, z: 0 })
|
||||||
? getNested(metadata, "logic.model.dimensions", { x: 1, y: 1, z: 0 })
|
: { x: 1, y: 1, z: 0 };
|
||||||
: { x: 1, y: 1, z: 0 }
|
const directions = metadata
|
||||||
) as { x: number; y: number; z: number };
|
? getNested(metadata, "logic.model.directions", [0])
|
||||||
const directions = (
|
: [0];
|
||||||
metadata ? getNested(metadata, "logic.model.directions", [0]) : [0]
|
|
||||||
) as number[];
|
|
||||||
|
|
||||||
const setDim = (axis: "x" | "y" | "z", value: string) => {
|
const setDim = (axis: "x" | "y" | "z", value: string) => {
|
||||||
const num =
|
const num =
|
||||||
@@ -245,11 +267,7 @@ export function NitroEditorDialog({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const toggleDirection = (dir: number) => {
|
const toggleDirection = (dir: number) => {
|
||||||
const current = getNested(
|
const current = getNested(metadata, "logic.model.directions", [0]);
|
||||||
metadata,
|
|
||||||
"logic.model.directions",
|
|
||||||
[0],
|
|
||||||
) as number[];
|
|
||||||
const next = current.includes(dir)
|
const next = current.includes(dir)
|
||||||
? current.filter((d: number) => d !== dir)
|
? current.filter((d: number) => d !== dir)
|
||||||
: [...current, dir].sort((a: number, b: number) => a - b);
|
: [...current, dir].sort((a: number, b: number) => a - b);
|
||||||
@@ -258,12 +276,9 @@ export function NitroEditorDialog({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Get visualization with size=64 for layer/color editing
|
// Get visualization with size=64 for layer/color editing
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: explicitly chosen here, see surrounding code
|
const viz64 = metadata?.visualizations?.find((v) => v.size === 64);
|
||||||
const viz64 = metadata?.visualizations?.find((v: any) => v.size === 64);
|
|
||||||
const viz64Index =
|
const viz64Index =
|
||||||
metadata?.visualizations?.findIndex(
|
metadata?.visualizations?.findIndex((v) => v.size === 64) ?? -1;
|
||||||
(v: { size: number }) => v.size === 64,
|
|
||||||
) ?? -1;
|
|
||||||
|
|
||||||
const layerCount = viz64?.layers ? Object.keys(viz64.layers).length : 0;
|
const layerCount = viz64?.layers ? Object.keys(viz64.layers).length : 0;
|
||||||
const colorCount = viz64?.colors ? Object.keys(viz64.colors).length : 0;
|
const colorCount = viz64?.colors ? Object.keys(viz64.colors).length : 0;
|
||||||
|
|||||||
@@ -4,12 +4,6 @@
|
|||||||
@plugin "tailwindcss-animate";
|
@plugin "tailwindcss-animate";
|
||||||
@custom-variant dark (&:where(.dark, .dark *));
|
@custom-variant dark (&:where(.dark, .dark *));
|
||||||
|
|
||||||
:root {
|
|
||||||
--font-nunito:
|
|
||||||
"Nunito", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
||||||
--font-pixel: "Pixelify Sans", monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--font-sans:
|
--font-sans:
|
||||||
var(--font-nunito), "Nunito", ui-sans-serif, system-ui, -apple-system,
|
var(--font-nunito), "Nunito", ui-sans-serif, system-ui, -apple-system,
|
||||||
|
|||||||
+20
-15
@@ -1,4 +1,5 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
import { Nunito, Pixelify_Sans } from "next/font/google";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
import { NextIntlClientProvider } from "next-intl";
|
import { NextIntlClientProvider } from "next-intl";
|
||||||
@@ -14,6 +15,20 @@ import { resolveHotelName } from "@/lib/hotel-name";
|
|||||||
import { siteSettings } from "@/lib/services/site-settings";
|
import { siteSettings } from "@/lib/services/site-settings";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
|
const nunito = Nunito({
|
||||||
|
subsets: ["latin"],
|
||||||
|
weight: ["400", "600", "700", "800"],
|
||||||
|
variable: "--font-nunito",
|
||||||
|
display: "swap",
|
||||||
|
});
|
||||||
|
|
||||||
|
const pixelify = Pixelify_Sans({
|
||||||
|
subsets: ["latin"],
|
||||||
|
weight: ["400", "700"],
|
||||||
|
variable: "--font-pixel",
|
||||||
|
display: "swap",
|
||||||
|
});
|
||||||
|
|
||||||
// The root layout reads runtime data (locale cookie, request headers via
|
// The root layout reads runtime data (locale cookie, request headers via
|
||||||
// enforceSiteAccess, DB-backed site settings) to render the <html> element,
|
// enforceSiteAccess, DB-backed site settings) to render the <html> element,
|
||||||
// which cannot be wrapped in <Suspense>. Child pages inherit this opt-out,
|
// which cannot be wrapped in <Suspense>. Child pages inherit this opt-out,
|
||||||
@@ -58,23 +73,13 @@ export default async function RootLayout({
|
|||||||
const nonce = (await headers()).get("x-nonce") ?? undefined;
|
const nonce = (await headers()).get("x-nonce") ?? undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang={locale} className="app" suppressHydrationWarning>
|
<html
|
||||||
|
lang={locale}
|
||||||
|
className={`app ${nunito.variable} ${pixelify.variable}`}
|
||||||
|
suppressHydrationWarning
|
||||||
|
>
|
||||||
<head>
|
<head>
|
||||||
<meta name="theme-default-dark" content={String(defaultDark)} />
|
<meta name="theme-default-dark" content={String(defaultDark)} />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
||||||
<link
|
|
||||||
rel="preconnect"
|
|
||||||
href="https://fonts.gstatic.com"
|
|
||||||
crossOrigin="anonymous"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800&display=swap"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Pixelify+Sans:wght@400;700&display=swap"
|
|
||||||
/>
|
|
||||||
<Script
|
<Script
|
||||||
src="/scripts/theme-init.js"
|
src="/scripts/theme-init.js"
|
||||||
strategy="beforeInteractive"
|
strategy="beforeInteractive"
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ import { useCallback, useEffect, useMemo, useState } from "react";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { updateCatalogPage } from "@/actions/catalog";
|
import { updateCatalogPage } from "@/actions/catalog";
|
||||||
import { updateBcPage } from "@/actions/catalog-bc";
|
import { updateBcPage } from "@/actions/catalog-bc";
|
||||||
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table";
|
import {
|
||||||
import { CatalogItemsTable } from "@/app/admin/catalog/[id]/catalog-items-table";
|
type CatalogItemData,
|
||||||
|
CatalogItemsTable,
|
||||||
|
} from "@/app/admin/catalog/[id]/catalog-items-table";
|
||||||
import { CatalogTranslateTab } from "@/app/admin/catalog/[id]/catalog-translate-tab";
|
import { CatalogTranslateTab } from "@/app/admin/catalog/[id]/catalog-translate-tab";
|
||||||
import { BulkImportItems } from "@/components/admin/catalog/bulk-import-items";
|
import { BulkImportItems } from "@/components/admin/catalog/bulk-import-items";
|
||||||
import { CatalogImagePicker } from "@/components/admin/catalog/catalog-image-picker";
|
import { CatalogImagePicker } from "@/components/admin/catalog/catalog-image-picker";
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Crown, Image as ImageIcon, Package, Sparkles } from "lucide-react";
|
import { Crown, Image as ImageIcon, Package, Sparkles } from "lucide-react";
|
||||||
import type { CatalogItemData } from "@/app/admin/catalog/[id]/catalog-items-table/types";
|
import {
|
||||||
import { getPointsLabel } from "@/app/admin/catalog/[id]/catalog-items-table/types";
|
type CatalogItemData,
|
||||||
|
getPointsLabel,
|
||||||
|
} from "@/app/admin/catalog/[id]/catalog-items-table/types";
|
||||||
import {
|
import {
|
||||||
CurrencyIcon,
|
CurrencyIcon,
|
||||||
currencyKindFromPointsType,
|
currencyKindFromPointsType,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { LucideIcon } from "lucide-react";
|
|
||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
@@ -19,6 +18,7 @@ import {
|
|||||||
Languages,
|
Languages,
|
||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
ListOrdered,
|
ListOrdered,
|
||||||
|
type LucideIcon,
|
||||||
Megaphone,
|
Megaphone,
|
||||||
Monitor,
|
Monitor,
|
||||||
Newspaper,
|
Newspaper,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { NextRequest } from "next/server";
|
import { type NextRequest, NextResponse } from "next/server";
|
||||||
import { NextResponse } from "next/server";
|
|
||||||
import { logAuthorizationEvent } from "@/lib/admin/authorization-events";
|
import { logAuthorizationEvent } from "@/lib/admin/authorization-events";
|
||||||
import { validateCsrfToken } from "@/lib/foundation/security";
|
import { validateCsrfToken } from "@/lib/foundation/security";
|
||||||
import { canAccess, getApiAdminContext } from "@/lib/permissions";
|
import { canAccess, getApiAdminContext } from "@/lib/permissions";
|
||||||
|
|||||||
@@ -45,9 +45,6 @@ describe("admin import backend contract", () => {
|
|||||||
expect(service).toContain("ExternalTexts.json");
|
expect(service).toContain("ExternalTexts.json");
|
||||||
expect(service).toContain("WebsiteBadges");
|
expect(service).toContain("WebsiteBadges");
|
||||||
expect(service).toContain("importBadgeSynced");
|
expect(service).toContain("importBadgeSynced");
|
||||||
expect(readFileSync("src/actions/import-badges.ts", "utf8")).toContain(
|
|
||||||
"importBadgeSynced",
|
|
||||||
);
|
|
||||||
expect(
|
expect(
|
||||||
readFileSync("src/app/api/admin/import/badges/route.ts", "utf8"),
|
readFileSync("src/app/api/admin/import/badges/route.ts", "utf8"),
|
||||||
).toContain("importBadgeSynced");
|
).toContain("importBadgeSynced");
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { promises as fs } from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { EffectMapEntry } from "@/types/effects";
|
||||||
|
import {
|
||||||
|
__setEffectMapPathForTests,
|
||||||
|
listLocalEffects,
|
||||||
|
mergeEffect,
|
||||||
|
readEffectMap,
|
||||||
|
removeEffect,
|
||||||
|
} from "./effectmap";
|
||||||
|
|
||||||
|
vi.mock("@/lib/services/site-settings", () => ({
|
||||||
|
siteSettings: { get: vi.fn(async () => "") },
|
||||||
|
}));
|
||||||
|
|
||||||
|
let dir: string;
|
||||||
|
let file: string;
|
||||||
|
|
||||||
|
const ENTRY: EffectMapEntry = {
|
||||||
|
id: "fx_1",
|
||||||
|
lib: "fx_1",
|
||||||
|
type: "fx",
|
||||||
|
revision: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
dir = await fs.mkdtemp(path.join(os.tmpdir(), "effectmap-"));
|
||||||
|
file = path.join(dir, "EffectMap.json");
|
||||||
|
await fs.writeFile(file, JSON.stringify({ effects: [ENTRY] }));
|
||||||
|
__setEffectMapPathForTests(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
__setEffectMapPathForTests(null);
|
||||||
|
await fs.rm(dir, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("lib/services/effectmap", () => {
|
||||||
|
it("lists local effects from the map", async () => {
|
||||||
|
const effects = await listLocalEffects();
|
||||||
|
expect(effects).toEqual([ENTRY]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an empty list when the file has no effects array", async () => {
|
||||||
|
await fs.writeFile(file, JSON.stringify({}));
|
||||||
|
expect(await readEffectMap()).toEqual({ effects: [] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds a new effect via mergeEffect", async () => {
|
||||||
|
const added: EffectMapEntry = {
|
||||||
|
id: "fx_2",
|
||||||
|
lib: "fx_2",
|
||||||
|
type: "fx",
|
||||||
|
revision: 2,
|
||||||
|
};
|
||||||
|
await mergeEffect(added);
|
||||||
|
const effects = await listLocalEffects();
|
||||||
|
expect(effects).toHaveLength(2);
|
||||||
|
expect(effects).toContainEqual(added);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates an existing effect in place via mergeEffect", async () => {
|
||||||
|
await mergeEffect({ ...ENTRY, revision: 5 });
|
||||||
|
const effects = await listLocalEffects();
|
||||||
|
expect(effects).toHaveLength(1);
|
||||||
|
expect(effects[0].revision).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes an effect by id", async () => {
|
||||||
|
expect(await removeEffect("fx_1")).toBe(true);
|
||||||
|
expect(await listLocalEffects()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes an effect by lib name", async () => {
|
||||||
|
expect(await removeEffect("fx_1")).toBe(true);
|
||||||
|
expect(await listLocalEffects()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reports false when nothing was removed", async () => {
|
||||||
|
expect(await removeEffect("missing")).toBe(false);
|
||||||
|
expect(await listLocalEffects()).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { promises as fs } from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { FigureLibrary } from "./figuremap";
|
||||||
|
import {
|
||||||
|
__setFigureMapPathForTests,
|
||||||
|
listFigureLibraries,
|
||||||
|
mergeLibrary,
|
||||||
|
} from "./figuremap";
|
||||||
|
|
||||||
|
vi.mock("@/lib/services/site-settings", () => ({
|
||||||
|
siteSettings: { get: vi.fn(async () => "") },
|
||||||
|
}));
|
||||||
|
|
||||||
|
let dir: string;
|
||||||
|
let file: string;
|
||||||
|
|
||||||
|
const LIB: FigureLibrary = {
|
||||||
|
id: "h_human",
|
||||||
|
revision: 1,
|
||||||
|
parts: [{ id: 1, type: "hd" }],
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
dir = await fs.mkdtemp(path.join(os.tmpdir(), "figuremap-"));
|
||||||
|
file = path.join(dir, "FigureMap.json");
|
||||||
|
await fs.writeFile(file, JSON.stringify({ libraries: [LIB] }));
|
||||||
|
__setFigureMapPathForTests(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
__setFigureMapPathForTests(null);
|
||||||
|
await fs.rm(dir, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("lib/services/figuremap", () => {
|
||||||
|
it("lists figure libraries from the map", async () => {
|
||||||
|
const libraries = await listFigureLibraries();
|
||||||
|
expect(libraries).toEqual([LIB]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an empty list when the file has no libraries array", async () => {
|
||||||
|
await fs.writeFile(file, JSON.stringify({}));
|
||||||
|
expect(await listFigureLibraries()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds a new library via mergeLibrary", async () => {
|
||||||
|
const added: FigureLibrary = { id: "hh_human", revision: 2 };
|
||||||
|
await mergeLibrary(added);
|
||||||
|
const libraries = await listFigureLibraries();
|
||||||
|
expect(libraries).toHaveLength(2);
|
||||||
|
expect(libraries).toContainEqual(added);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates an existing library in place via mergeLibrary", async () => {
|
||||||
|
await mergeLibrary({ ...LIB, revision: 9 });
|
||||||
|
const libraries = await listFigureLibraries();
|
||||||
|
expect(libraries).toHaveLength(1);
|
||||||
|
expect(libraries[0].revision).toBe(9);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -4,23 +4,6 @@ import { and, eq, type SQL, sql } from "drizzle-orm";
|
|||||||
import { CatalogPages, db, ItemsBase } from "@/lib/db";
|
import { CatalogPages, db, ItemsBase } from "@/lib/db";
|
||||||
import { officialHabboEnrichmentWarning } from "@/lib/habbo-gamedata-hotel";
|
import { officialHabboEnrichmentWarning } from "@/lib/habbo-gamedata-hotel";
|
||||||
import { logger } from "@/lib/logger";
|
import { logger } from "@/lib/logger";
|
||||||
import {
|
|
||||||
appendFurniEntry,
|
|
||||||
buildFurniEntry,
|
|
||||||
removeFurniEntry,
|
|
||||||
} from "@/lib/services/furni-data";
|
|
||||||
import type { OfficialHabboFurniEntry } from "@/lib/services/habbo-furnidata-cache";
|
|
||||||
import { lookupOfficialHabboFurni } from "@/lib/services/habbo-furnidata-cache";
|
|
||||||
import { getHabboGamedataHotel } from "@/lib/services/habbo-gamedata-hotel";
|
|
||||||
|
|
||||||
// Re-export the download helpers now owned by the shared import core.
|
|
||||||
// Existing furni callers keep importing them from here.
|
|
||||||
export {
|
|
||||||
downloadFile,
|
|
||||||
validatePngBytes,
|
|
||||||
validateSwfBytes,
|
|
||||||
} from "@/lib/services/import/core/download";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_FURNI_ICON_DIR,
|
DEFAULT_FURNI_ICON_DIR,
|
||||||
DEFAULT_FURNI_NITRO_DIR,
|
DEFAULT_FURNI_NITRO_DIR,
|
||||||
@@ -28,14 +11,32 @@ import {
|
|||||||
getFurniAssetDirs,
|
getFurniAssetDirs,
|
||||||
getFurniAssetWriteTargets,
|
getFurniAssetWriteTargets,
|
||||||
} from "@/lib/services/furni-asset-dirs";
|
} from "@/lib/services/furni-asset-dirs";
|
||||||
import { downloadFile } from "@/lib/services/import/core/download";
|
|
||||||
import type { ConversionResult } from "@/lib/services/swf-to-nitro";
|
|
||||||
import {
|
import {
|
||||||
|
appendFurniEntry,
|
||||||
|
buildFurniEntry,
|
||||||
|
removeFurniEntry,
|
||||||
|
} from "@/lib/services/furni-data";
|
||||||
|
import {
|
||||||
|
lookupOfficialHabboFurni,
|
||||||
|
type OfficialHabboFurniEntry,
|
||||||
|
} from "@/lib/services/habbo-furnidata-cache";
|
||||||
|
import { getHabboGamedataHotel } from "@/lib/services/habbo-gamedata-hotel";
|
||||||
|
import {
|
||||||
|
downloadFile,
|
||||||
|
validatePngBytes,
|
||||||
|
validateSwfBytes,
|
||||||
|
} from "@/lib/services/import/core/download";
|
||||||
|
import {
|
||||||
|
type ConversionResult,
|
||||||
convertSwfToNitro,
|
convertSwfToNitro,
|
||||||
extractIconFromSwf,
|
extractIconFromSwf,
|
||||||
} from "@/lib/services/swf-to-nitro";
|
} from "@/lib/services/swf-to-nitro";
|
||||||
import type { ImportSingleResult } from "@/types/furni";
|
import type { ImportSingleResult } from "@/types/furni";
|
||||||
|
|
||||||
|
// Re-export the download helpers now owned by the shared import core.
|
||||||
|
// Existing furni callers keep importing them from here.
|
||||||
|
export { downloadFile, validatePngBytes, validateSwfBytes };
|
||||||
|
|
||||||
// ── Constants ────────────────────────────────────────────────────────
|
// ── Constants ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
// turbopackIgnore hints: this module is reached by App Router routes; without
|
// turbopackIgnore hints: this module is reached by App Router routes; without
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { CurrencyDb } from "./send-currency";
|
import { type CurrencyDb, sendCurrency } from "./send-currency";
|
||||||
|
|
||||||
vi.mock("@/lib/db", () => ({
|
vi.mock("@/lib/db", () => ({
|
||||||
db: {},
|
db: {},
|
||||||
@@ -9,8 +9,6 @@ vi.mock("@/lib/db", () => ({
|
|||||||
|
|
||||||
vi.mock("@/lib/services/rcon", () => ({}));
|
vi.mock("@/lib/services/rcon", () => ({}));
|
||||||
|
|
||||||
import { sendCurrency } from "./send-currency";
|
|
||||||
|
|
||||||
const mockDb = {} as CurrencyDb;
|
const mockDb = {} as CurrencyDb;
|
||||||
|
|
||||||
describe("sendCurrency", () => {
|
describe("sendCurrency", () => {
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import { deflateSync } from "node:zlib";
|
||||||
|
import LZMA from "lzma";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import {
|
||||||
|
decompressSwf,
|
||||||
|
parseBinaryData,
|
||||||
|
parseSwfTags,
|
||||||
|
parseSymbolClass,
|
||||||
|
TAG_DEFINE_BINARY_DATA,
|
||||||
|
TAG_DEFINE_BITS_LOSSLESS,
|
||||||
|
TAG_END,
|
||||||
|
} from "./swf-parser";
|
||||||
|
|
||||||
|
const BODY = Buffer.from(
|
||||||
|
"uncompressed swf body used for decompression fixtures, padded to a reasonable length",
|
||||||
|
);
|
||||||
|
|
||||||
|
function fwsHeader(payload: Buffer, version = 6): Buffer {
|
||||||
|
const header = Buffer.alloc(8);
|
||||||
|
header.write("FWS", 0, "ascii");
|
||||||
|
header.writeUInt8(version, 3);
|
||||||
|
header.writeUInt32LE(payload.length + 8, 4);
|
||||||
|
return Buffer.concat([header, payload]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cwsFixture(payload: Buffer, version = 6): Buffer {
|
||||||
|
const header = Buffer.alloc(8);
|
||||||
|
header.write("CWS", 0, "ascii");
|
||||||
|
header.writeUInt8(version, 3);
|
||||||
|
header.writeUInt32LE(payload.length + 8, 4);
|
||||||
|
const compressed = deflateSync(payload);
|
||||||
|
return Buffer.concat([header, compressed]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function zwsFixture(payload: Buffer, version = 6): Buffer {
|
||||||
|
const rawLzma = LZMA.compress(payload, 9);
|
||||||
|
const props = Buffer.from(rawLzma.slice(0, 5));
|
||||||
|
const compressedPayload = Buffer.from(rawLzma.slice(13));
|
||||||
|
const compressedSize = 5 + compressedPayload.length;
|
||||||
|
const header = Buffer.alloc(12);
|
||||||
|
header.write("ZWS", 0, "ascii");
|
||||||
|
header.writeUInt8(version, 3);
|
||||||
|
header.writeUInt32LE(payload.length + 8, 4);
|
||||||
|
header.writeUInt32LE(compressedSize, 8);
|
||||||
|
return Buffer.concat([header, props, compressedPayload]);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("decompressSwf", () => {
|
||||||
|
it("returns FWS buffers untouched", () => {
|
||||||
|
const fws = fwsHeader(BODY);
|
||||||
|
expect(decompressSwf(fws)).toBe(fws);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("inflates CWS (zlib) buffers and restores the FWS signature", () => {
|
||||||
|
const result = decompressSwf(cwsFixture(BODY));
|
||||||
|
expect(result.toString("ascii", 0, 3)).toBe("FWS");
|
||||||
|
expect(result.length).toBe(BODY.length + 8);
|
||||||
|
expect(result.subarray(8).equals(BODY)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("decompresses ZWS (lzma) buffers and restores the FWS signature", () => {
|
||||||
|
const result = decompressSwf(zwsFixture(BODY));
|
||||||
|
expect(result.toString("ascii", 0, 3)).toBe("FWS");
|
||||||
|
expect(result.length).toBe(BODY.length + 8);
|
||||||
|
expect(result.subarray(8).equals(BODY)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws on unknown signatures", () => {
|
||||||
|
const bad = Buffer.alloc(16);
|
||||||
|
bad.write("XYZ", 0, "ascii");
|
||||||
|
expect(() => decompressSwf(bad)).toThrow("Unknown SWF signature");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function shortTag(tagType: number, data: Buffer): Buffer {
|
||||||
|
expect(data.length).toBeLessThan(0x3f);
|
||||||
|
const tag = Buffer.alloc(2 + data.length);
|
||||||
|
tag.writeUInt16LE((tagType << 6) | data.length, 0);
|
||||||
|
data.copy(tag, 2);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
function swfWithTags(tags: Buffer[]): Buffer {
|
||||||
|
const tagStream = Buffer.concat([...tags, Buffer.from([0, 0])]);
|
||||||
|
const body = Buffer.alloc(13);
|
||||||
|
body.write("FWS", 0, "ascii");
|
||||||
|
body.writeUInt8(6, 3);
|
||||||
|
body.writeUInt32LE(tagStream.length + 13, 4);
|
||||||
|
return Buffer.concat([body, tagStream]);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("parseSwfTags", () => {
|
||||||
|
it("parses short-form tags until the end marker", () => {
|
||||||
|
const swf = swfWithTags([
|
||||||
|
shortTag(TAG_DEFINE_BITS_LOSSLESS, Buffer.from([1, 2, 3])),
|
||||||
|
shortTag(TAG_DEFINE_BINARY_DATA, Buffer.from([9, 9, 9])),
|
||||||
|
]);
|
||||||
|
const tags = parseSwfTags(swf);
|
||||||
|
expect(tags).toHaveLength(2);
|
||||||
|
expect(tags[0].type).toBe(TAG_DEFINE_BITS_LOSSLESS);
|
||||||
|
expect(tags[0].data.equals(Buffer.from([1, 2, 3]))).toBe(true);
|
||||||
|
expect(tags[1].type).toBe(TAG_DEFINE_BINARY_DATA);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("stops at TAG_END and ignores trailing bytes", () => {
|
||||||
|
const swf = swfWithTags([
|
||||||
|
shortTag(TAG_DEFINE_BITS_LOSSLESS, Buffer.from([1])),
|
||||||
|
]);
|
||||||
|
expect(parseSwfTags(swf)).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an empty array for an empty tag stream", () => {
|
||||||
|
const empty = Buffer.alloc(13);
|
||||||
|
empty.write("FWS", 0, "ascii");
|
||||||
|
expect(parseSwfTags(empty)).toEqual([]);
|
||||||
|
expect(TAG_END).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("parseSymbolClass", () => {
|
||||||
|
it("maps char ids to names and tracks aliases", () => {
|
||||||
|
const data = Buffer.concat([
|
||||||
|
Buffer.from([2, 0]),
|
||||||
|
Buffer.from([1, 0]),
|
||||||
|
Buffer.from("chair\0", "utf8"),
|
||||||
|
Buffer.from([2, 0]),
|
||||||
|
Buffer.from("table\0", "utf8"),
|
||||||
|
]);
|
||||||
|
const result = parseSymbolClass(data);
|
||||||
|
expect(result.classNames.get(1)).toBe("chair");
|
||||||
|
expect(result.classNames.get(2)).toBe("table");
|
||||||
|
expect(result.symbols.get("chair")).toBe(1);
|
||||||
|
expect(result.symbols.get("table")).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps the first name for a repeated char id", () => {
|
||||||
|
const data = Buffer.concat([
|
||||||
|
Buffer.from([2, 0]),
|
||||||
|
Buffer.from([1, 0]),
|
||||||
|
Buffer.from("chair\0", "utf8"),
|
||||||
|
Buffer.from([1, 0]),
|
||||||
|
Buffer.from("lounge_chair\0", "utf8"),
|
||||||
|
]);
|
||||||
|
const result = parseSymbolClass(data);
|
||||||
|
expect(result.classNames.get(1)).toBe("chair");
|
||||||
|
expect(result.symbols.get("lounge_chair")).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("parseBinaryData", () => {
|
||||||
|
it("reads the char id and strips the 6-byte attribute header", () => {
|
||||||
|
const data = Buffer.concat([
|
||||||
|
Buffer.from([42, 0]),
|
||||||
|
Buffer.from([1, 2, 3, 4]),
|
||||||
|
Buffer.from("binary payload"),
|
||||||
|
]);
|
||||||
|
const result = parseBinaryData(data);
|
||||||
|
expect(result.id).toBe(42);
|
||||||
|
expect(result.content.equals(Buffer.from("binary payload"))).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
+1
-1
@@ -32,5 +32,5 @@
|
|||||||
".next/types/**/*.ts",
|
".next/types/**/*.ts",
|
||||||
".next/dev/types/**/*.ts"
|
".next/dev/types/**/*.ts"
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules", "e2e", "playwright.config.ts"]
|
"exclude": ["node_modules", "e2e", "playwright.config.ts", "src/generated"]
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user