mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-02-04 15:39:45 +00:00
refactor: upgrade to Zod v4 (#623)
This commit is contained in:
@@ -144,8 +144,6 @@
|
|||||||
"expires_at": "Expires At",
|
"expires_at": "Expires At",
|
||||||
"when_this_api_key_will_expire": "When this API key will expire.",
|
"when_this_api_key_will_expire": "When this API key will expire.",
|
||||||
"optional_description_to_help_identify_this_keys_purpose": "Optional description to help identify this key's purpose.",
|
"optional_description_to_help_identify_this_keys_purpose": "Optional description to help identify this key's purpose.",
|
||||||
"name_must_be_at_least_3_characters": "Name must be at least 3 characters",
|
|
||||||
"name_cannot_exceed_50_characters": "Name cannot exceed 50 characters",
|
|
||||||
"expiration_date_must_be_in_the_future": "Expiration date must be in the future",
|
"expiration_date_must_be_in_the_future": "Expiration date must be in the future",
|
||||||
"revoke_api_key": "Revoke API Key",
|
"revoke_api_key": "Revoke API Key",
|
||||||
"never": "Never",
|
"never": "Never",
|
||||||
|
|||||||
182
frontend/package-lock.json
generated
182
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "pocket-id-frontend",
|
"name": "pocket-id-frontend",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pocket-id-frontend",
|
"name": "pocket-id-frontend",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lucide/svelte": "^0.511.0",
|
"@lucide/svelte": "^0.511.0",
|
||||||
"@simplewebauthn/browser": "^13.1.0",
|
"@simplewebauthn/browser": "^13.1.0",
|
||||||
@@ -16,9 +16,10 @@
|
|||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"jose": "^5.9.6",
|
"jose": "^5.9.6",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
|
"rollup-plugin-visualizer": "^6.0.1",
|
||||||
"sveltekit-superforms": "^2.23.1",
|
"sveltekit-superforms": "^2.23.1",
|
||||||
"tailwind-merge": "^3.3.0",
|
"tailwind-merge": "^3.3.0",
|
||||||
"zod": "^3.24.1"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@inlang/paraglide-js": "^2.0.13",
|
"@inlang/paraglide-js": "^2.0.13",
|
||||||
@@ -2412,6 +2413,15 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/define-lazy-prop": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/delayed-stream": {
|
"node_modules/delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
@@ -2536,6 +2546,15 @@
|
|||||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/escalade": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/escape-string-regexp": {
|
"node_modules/escape-string-regexp": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
@@ -3156,6 +3175,21 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/is-docker": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"is-docker": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-extglob": {
|
"node_modules/is-extglob": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
@@ -3195,6 +3229,18 @@
|
|||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-wsl": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-docker": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/isexe": {
|
"node_modules/isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
@@ -3791,6 +3837,23 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/open": {
|
||||||
|
"version": "8.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
|
||||||
|
"integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"define-lazy-prop": "^2.0.0",
|
||||||
|
"is-docker": "^2.1.1",
|
||||||
|
"is-wsl": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@@ -4356,6 +4419,112 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rollup-plugin-visualizer": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-NjlGElvLXCSZSAi3gNRZbfX3qlQbQcJ9TW97c5JpqfVwMhttj9YwEdPwcvbKj91RnMX2PWAjonvSEv6UEYtnRQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"open": "^8.0.0",
|
||||||
|
"picomatch": "^4.0.2",
|
||||||
|
"source-map": "^0.7.4",
|
||||||
|
"yargs": "^17.5.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rollup-plugin-visualizer": "dist/bin/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rolldown": "1.x",
|
||||||
|
"rollup": "2.x || 3.x || 4.x"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rolldown": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup-plugin-visualizer/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==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"strip-ansi": "^6.0.1",
|
||||||
|
"wrap-ansi": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup-plugin-visualizer/node_modules/source-map": {
|
||||||
|
"version": "0.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
||||||
|
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup-plugin-visualizer/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==",
|
||||||
|
"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/rollup-plugin-visualizer/node_modules/y18n": {
|
||||||
|
"version": "5.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup-plugin-visualizer/node_modules/yargs": {
|
||||||
|
"version": "17.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||||
|
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||||
|
"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/rollup-plugin-visualizer/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==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/run-parallel": {
|
"node_modules/run-parallel": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
@@ -5458,9 +5627,10 @@
|
|||||||
"integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="
|
"integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="
|
||||||
},
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "3.24.1",
|
"version": "3.25.55",
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.55.tgz",
|
||||||
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
|
"integrity": "sha512-219huNnkSLQnLsQ3uaRjXsxMrVm5C9W3OOpEVt2k5tvMKuA8nBSu38e0B//a+he9Iq2dvmk2VyYVlHqiHa4YBA==",
|
||||||
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"sveltekit-superforms": "^2.23.1",
|
"sveltekit-superforms": "^2.23.1",
|
||||||
"tailwind-merge": "^3.3.0",
|
"tailwind-merge": "^3.3.0",
|
||||||
"zod": "^3.24.1"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@inlang/paraglide-js": "^2.0.13",
|
"@inlang/paraglide-js": "^2.0.13",
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { setLocale } from '$lib/paraglide/runtime';
|
|
||||||
import type { User } from '$lib/types/user.type';
|
import type { User } from '$lib/types/user.type';
|
||||||
|
import { setLocale } from '$lib/utils/locale.util';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
const userStore = writable<User | null>(null);
|
const userStore = writable<User | null>(null);
|
||||||
|
|
||||||
const setUser = (user: User) => {
|
const setUser = (user: User) => {
|
||||||
if (user.locale) {
|
if (user.locale) {
|
||||||
setLocale(user.locale, { reload: false });
|
setLocale(user.locale, false);
|
||||||
}
|
}
|
||||||
userStore.set(user);
|
userStore.set(user);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export type OidcClientFederatedIdentity = {
|
|||||||
issuer: string;
|
issuer: string;
|
||||||
subject?: string;
|
subject?: string;
|
||||||
audience?: string;
|
audience?: string;
|
||||||
jwks?: string;
|
jwks: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OidcClientCredentials = {
|
export type OidcClientCredentials = {
|
||||||
@@ -18,7 +18,7 @@ export type OidcClientCredentials = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type OidcClient = OidcClientMetaData & {
|
export type OidcClient = OidcClientMetaData & {
|
||||||
callbackURLs: string[]; // No longer requires at least one URL
|
callbackURLs: string[];
|
||||||
logoutCallbackURLs: string[];
|
logoutCallbackURLs: string[];
|
||||||
isPublic: boolean;
|
isPublic: boolean;
|
||||||
pkceEnabled: boolean;
|
pkceEnabled: boolean;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { writable } from 'svelte/store';
|
import { get, writable } from 'svelte/store';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
export type FormInput<T> = {
|
export type FormInput<T> = {
|
||||||
value: T;
|
value: T;
|
||||||
@@ -13,6 +13,7 @@ type FormInputs<T> = {
|
|||||||
export function createForm<T extends z.ZodType<any, any>>(schema: T, initialValues: z.infer<T>) {
|
export function createForm<T extends z.ZodType<any, any>>(schema: T, initialValues: z.infer<T>) {
|
||||||
// Create a writable store for the inputs
|
// Create a writable store for the inputs
|
||||||
const inputsStore = writable<FormInputs<z.infer<T>>>(initializeInputs(initialValues));
|
const inputsStore = writable<FormInputs<z.infer<T>>>(initializeInputs(initialValues));
|
||||||
|
const errorsStore = writable<z.ZodError<any> | undefined>();
|
||||||
|
|
||||||
function initializeInputs(initialValues: z.infer<T>): FormInputs<z.infer<T>> {
|
function initializeInputs(initialValues: z.infer<T>): FormInputs<z.infer<T>> {
|
||||||
const inputs: FormInputs<z.infer<T>> = {} as FormInputs<z.infer<T>>;
|
const inputs: FormInputs<z.infer<T>> = {} as FormInputs<z.infer<T>>;
|
||||||
@@ -36,11 +37,12 @@ export function createForm<T extends z.ZodType<any, any>>(schema: T, initialValu
|
|||||||
);
|
);
|
||||||
|
|
||||||
const result = schema.safeParse(values);
|
const result = schema.safeParse(values);
|
||||||
|
errorsStore.set(result.error);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
success = false;
|
success = false;
|
||||||
for (const input of Object.keys(inputs)) {
|
for (const input of Object.keys(inputs)) {
|
||||||
const error = result.error.errors.find((e) => e.path[0] === input);
|
const error = result.error.issues.find((e) => e.path[0] === input);
|
||||||
if (error) {
|
if (error) {
|
||||||
inputs[input as keyof z.infer<T>].error = error.message;
|
inputs[input as keyof z.infer<T>].error = error.message;
|
||||||
} else {
|
} else {
|
||||||
@@ -58,15 +60,14 @@ export function createForm<T extends z.ZodType<any, any>>(schema: T, initialValu
|
|||||||
}
|
}
|
||||||
|
|
||||||
function data() {
|
function data() {
|
||||||
let values: z.infer<T> | null = null;
|
const inputs = get(inputsStore);
|
||||||
inputsStore.subscribe((inputs) => {
|
|
||||||
values = Object.fromEntries(
|
const values = Object.fromEntries(
|
||||||
Object.entries(inputs).map(([key, input]) => {
|
Object.entries(inputs).map(([key, input]) => {
|
||||||
input.value = trimValue(input.value);
|
input.value = trimValue(input.value);
|
||||||
return [key, input.value];
|
return [key, input.value];
|
||||||
})
|
})
|
||||||
) as z.infer<T>;
|
) as z.infer<T>;
|
||||||
})();
|
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
@@ -108,6 +109,7 @@ export function createForm<T extends z.ZodType<any, any>>(schema: T, initialValu
|
|||||||
return {
|
return {
|
||||||
schema,
|
schema,
|
||||||
inputs: inputsStore,
|
inputs: inputsStore,
|
||||||
|
errors: errorsStore,
|
||||||
data,
|
data,
|
||||||
validate,
|
validate,
|
||||||
setValue,
|
setValue,
|
||||||
|
|||||||
10
frontend/src/lib/utils/locale.util.ts
Normal file
10
frontend/src/lib/utils/locale.util.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { setLocale as setParaglideLocale, type Locale } from '$lib/paraglide/runtime';
|
||||||
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
|
export function setLocale(locale: Locale, reload = true) {
|
||||||
|
import(`../../../node_modules/zod/dist/esm/v4/locales/${locale}.js`)
|
||||||
|
.then((zodLocale) => z.config(zodLocale.default()))
|
||||||
|
.finally(() => {
|
||||||
|
setParaglideLocale(locale, { reload });
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
import { preventDefault } from '$lib/utils/event-util';
|
import { preventDefault } from '$lib/utils/event-util';
|
||||||
import { createForm } from '$lib/utils/form-util';
|
import { createForm } from '$lib/utils/form-util';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
callback,
|
callback,
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
.min(2)
|
.min(2)
|
||||||
.max(30)
|
.max(30)
|
||||||
.regex(/^[a-z0-9_@.-]+$/, m.username_can_only_contain()),
|
.regex(/^[a-z0-9_@.-]+$/, m.username_can_only_contain()),
|
||||||
email: z.string().email(),
|
email: z.email(),
|
||||||
isAdmin: z.boolean()
|
isAdmin: z.boolean()
|
||||||
});
|
});
|
||||||
type FormSchema = typeof formSchema;
|
type FormSchema = typeof formSchema;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import * as Select from '$lib/components/ui/select';
|
import * as Select from '$lib/components/ui/select';
|
||||||
import { getLocale, setLocale, type Locale } from '$lib/paraglide/runtime';
|
import { getLocale, type Locale } from '$lib/paraglide/runtime';
|
||||||
import UserService from '$lib/services/user-service';
|
import UserService from '$lib/services/user-service';
|
||||||
import userStore from '$lib/stores/user-store';
|
import userStore from '$lib/stores/user-store';
|
||||||
|
import { setLocale } from '$lib/utils/locale.util';
|
||||||
|
|
||||||
const userService = new UserService();
|
const userService = new UserService();
|
||||||
const currentLocale = getLocale();
|
const currentLocale = getLocale();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import type { ApiKeyCreate } from '$lib/types/api-key.type';
|
import type { ApiKeyCreate } from '$lib/types/api-key.type';
|
||||||
import { preventDefault } from '$lib/utils/event-util';
|
import { preventDefault } from '$lib/utils/event-util';
|
||||||
import { createForm } from '$lib/utils/form-util';
|
import { createForm } from '$lib/utils/form-util';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
callback
|
callback
|
||||||
@@ -28,8 +28,8 @@
|
|||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
name: z
|
name: z
|
||||||
.string()
|
.string()
|
||||||
.min(3, m.name_must_be_at_least_3_characters())
|
.min(3)
|
||||||
.max(50, m.name_cannot_exceed_50_characters()),
|
.max(50),
|
||||||
description: z.string().default(''),
|
description: z.string().default(''),
|
||||||
expiresAt: z.date().min(new Date(), m.expiration_date_must_be_in_the_future())
|
expiresAt: z.date().min(new Date(), m.expiration_date_must_be_in_the_future())
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
import { preventDefault } from '$lib/utils/event-util';
|
import { preventDefault } from '$lib/utils/event-util';
|
||||||
import { createForm } from '$lib/utils/form-util';
|
import { createForm } from '$lib/utils/form-util';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
callback,
|
callback,
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
smtpPort: z.number().min(1),
|
smtpPort: z.number().min(1),
|
||||||
smtpUser: z.string(),
|
smtpUser: z.string(),
|
||||||
smtpPassword: z.string(),
|
smtpPassword: z.string(),
|
||||||
smtpFrom: z.string().email(),
|
smtpFrom: z.email(),
|
||||||
smtpTls: z.enum(['none', 'starttls', 'tls']),
|
smtpTls: z.enum(['none', 'starttls', 'tls']),
|
||||||
smtpSkipCertVerify: z.boolean(),
|
smtpSkipCertVerify: z.boolean(),
|
||||||
emailOneTimeAccessAsUnauthenticatedEnabled: z.boolean(),
|
emailOneTimeAccessAsUnauthenticatedEnabled: z.boolean(),
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import { preventDefault } from '$lib/utils/event-util';
|
import { preventDefault } from '$lib/utils/event-util';
|
||||||
import { createForm } from '$lib/utils/form-util';
|
import { createForm } from '$lib/utils/form-util';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
callback,
|
callback,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import { preventDefault } from '$lib/utils/event-util';
|
import { preventDefault } from '$lib/utils/event-util';
|
||||||
import { createForm } from '$lib/utils/form-util';
|
import { createForm } from '$lib/utils/form-util';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
callback,
|
callback,
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
ldapUrl: z.string().url(),
|
ldapUrl: z.url(),
|
||||||
ldapBindDn: z.string().min(1),
|
ldapBindDn: z.string().min(1),
|
||||||
ldapBindPassword: z.string().min(1),
|
ldapBindPassword: z.string().min(1),
|
||||||
ldapBase: z.string().min(1),
|
ldapBase: z.string().min(1),
|
||||||
|
|||||||
@@ -8,16 +8,18 @@
|
|||||||
import { LucideMinus, LucidePlus } from '@lucide/svelte';
|
import { LucideMinus, LucidePlus } from '@lucide/svelte';
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
import type { HTMLAttributes } from 'svelte/elements';
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
client,
|
client,
|
||||||
federatedIdentities = $bindable([]),
|
federatedIdentities = $bindable([]),
|
||||||
error = $bindable(null),
|
errors,
|
||||||
...restProps
|
...restProps
|
||||||
}: HTMLAttributes<HTMLDivElement> & {
|
}: HTMLAttributes<HTMLDivElement> & {
|
||||||
client?: OidcClient;
|
client?: OidcClient;
|
||||||
federatedIdentities: OidcClientFederatedIdentity[];
|
federatedIdentities: OidcClientFederatedIdentity[];
|
||||||
error?: string | null;
|
errors?: z.core.$ZodIssue[];
|
||||||
|
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
@@ -47,6 +49,13 @@
|
|||||||
[field]: value
|
[field]: value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFieldError(index: number, field: keyof OidcClientFederatedIdentity): string | null {
|
||||||
|
console.log(federatedIdentities)
|
||||||
|
if (!errors) return null;
|
||||||
|
const path = [index, field];
|
||||||
|
return errors?.filter((e) => e.path[0] == path[0] && e.path[1] == path[1])[0]?.message;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div {...restProps}>
|
<div {...restProps}>
|
||||||
@@ -76,8 +85,11 @@
|
|||||||
placeholder="https://example.com/"
|
placeholder="https://example.com/"
|
||||||
value={identity.issuer}
|
value={identity.issuer}
|
||||||
oninput={(e) => updateFederatedIdentity(i, 'issuer', e.currentTarget.value)}
|
oninput={(e) => updateFederatedIdentity(i, 'issuer', e.currentTarget.value)}
|
||||||
required
|
aria-invalid={!!getFieldError(i, 'issuer')}
|
||||||
/>
|
/>
|
||||||
|
{#if getFieldError(i, 'issuer')}
|
||||||
|
<p class="text-destructive mt-1 text-xs">{getFieldError(i, 'issuer')}</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -87,7 +99,11 @@
|
|||||||
placeholder="Defaults to the client ID: {client?.id}"
|
placeholder="Defaults to the client ID: {client?.id}"
|
||||||
value={identity.subject || ''}
|
value={identity.subject || ''}
|
||||||
oninput={(e) => updateFederatedIdentity(i, 'subject', e.currentTarget.value)}
|
oninput={(e) => updateFederatedIdentity(i, 'subject', e.currentTarget.value)}
|
||||||
|
aria-invalid={!!getFieldError(i, 'subject')}
|
||||||
/>
|
/>
|
||||||
|
{#if getFieldError(i, 'subject')}
|
||||||
|
<p class="text-destructive mt-1 text-xs">{getFieldError(i, 'subject')}</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -97,7 +113,11 @@
|
|||||||
placeholder="Defaults to the Pocket ID URL"
|
placeholder="Defaults to the Pocket ID URL"
|
||||||
value={identity.audience || ''}
|
value={identity.audience || ''}
|
||||||
oninput={(e) => updateFederatedIdentity(i, 'audience', e.currentTarget.value)}
|
oninput={(e) => updateFederatedIdentity(i, 'audience', e.currentTarget.value)}
|
||||||
|
aria-invalid={!!getFieldError(i, 'audience')}
|
||||||
/>
|
/>
|
||||||
|
{#if getFieldError(i, 'audience')}
|
||||||
|
<p class="text-destructive mt-1 text-xs">{getFieldError(i, 'audience')}</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -107,7 +127,11 @@
|
|||||||
placeholder="Defaults to {identity.issuer || '<issuer>'}/.well-known/jwks.json"
|
placeholder="Defaults to {identity.issuer || '<issuer>'}/.well-known/jwks.json"
|
||||||
value={identity.jwks || ''}
|
value={identity.jwks || ''}
|
||||||
oninput={(e) => updateFederatedIdentity(i, 'jwks', e.currentTarget.value)}
|
oninput={(e) => updateFederatedIdentity(i, 'jwks', e.currentTarget.value)}
|
||||||
|
aria-invalid={!!getFieldError(i, 'jwks')}
|
||||||
/>
|
/>
|
||||||
|
{#if getFieldError(i, 'jwks')}
|
||||||
|
<p class="text-destructive mt-1 text-xs">{getFieldError(i, 'jwks')}</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,10 +139,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
{#if error}
|
|
||||||
<p class="text-destructive mt-1 text-xs">{error}</p>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<Button class="mt-3" variant="secondary" size="sm" onclick={addFederatedIdentity} type="button">
|
<Button class="mt-3" variant="secondary" size="sm" onclick={addFederatedIdentity} type="button">
|
||||||
<LucidePlus class="mr-1 size-4" />
|
<LucidePlus class="mr-1 size-4" />
|
||||||
{federatedIdentities.length === 0
|
{federatedIdentities.length === 0
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
import { cn } from '$lib/utils/style';
|
import { cn } from '$lib/utils/style';
|
||||||
import { LucideChevronDown } from '@lucide/svelte';
|
import { LucideChevronDown } from '@lucide/svelte';
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod/v4';
|
||||||
import FederatedIdentitiesInput from './federated-identities-input.svelte';
|
import FederatedIdentitiesInput from './federated-identities-input.svelte';
|
||||||
import OidcCallbackUrlInput from './oidc-callback-url-input.svelte';
|
import OidcCallbackUrlInput from './oidc-callback-url-input.svelte';
|
||||||
|
|
||||||
@@ -50,17 +50,17 @@
|
|||||||
credentials: z.object({
|
credentials: z.object({
|
||||||
federatedIdentities: z.array(
|
federatedIdentities: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
issuer: z.string().url(),
|
issuer: z.url(),
|
||||||
subject: z.string().optional(),
|
subject: z.string().optional(),
|
||||||
audience: z.string().optional(),
|
audience: z.string().optional(),
|
||||||
jwks: z.string().url().optional().or(z.literal(''))
|
jwks: z.url().optional().or(z.literal(''))
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
type FormSchema = typeof formSchema;
|
type FormSchema = typeof formSchema;
|
||||||
const { inputs, ...form } = createForm<FormSchema>(formSchema, client);
|
const { inputs, errors, ...form } = createForm<FormSchema>(formSchema, client);
|
||||||
|
|
||||||
async function onSubmit() {
|
async function onSubmit() {
|
||||||
const data = form.validate();
|
const data = form.validate();
|
||||||
@@ -91,6 +91,15 @@
|
|||||||
logo = null;
|
logo = null;
|
||||||
logoDataURL = null;
|
logoDataURL = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFederatedIdentityErrors(errors: z.ZodError<any> | undefined) {
|
||||||
|
return errors?.issues
|
||||||
|
.filter((e) => e.path[0] == 'credentials' && e.path[1] == 'federatedIdentities')
|
||||||
|
.map((e) => {
|
||||||
|
e.path.splice(0, 2);
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form onsubmit={preventDefault(onSubmit)}>
|
<form onsubmit={preventDefault(onSubmit)}>
|
||||||
@@ -159,7 +168,7 @@
|
|||||||
<FederatedIdentitiesInput
|
<FederatedIdentitiesInput
|
||||||
client={existingClient}
|
client={existingClient}
|
||||||
bind:federatedIdentities={$inputs.credentials.value.federatedIdentities}
|
bind:federatedIdentities={$inputs.credentials.value.federatedIdentities}
|
||||||
bind:error={$inputs.credentials.error}
|
errors={getFederatedIdentityErrors($errors)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import type { UserGroupCreate } from '$lib/types/user-group.type';
|
import type { UserGroupCreate } from '$lib/types/user-group.type';
|
||||||
import { preventDefault } from '$lib/utils/event-util';
|
import { preventDefault } from '$lib/utils/event-util';
|
||||||
import { createForm } from '$lib/utils/form-util';
|
import { createForm } from '$lib/utils/form-util';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
callback,
|
callback,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import type { User, UserCreate } from '$lib/types/user.type';
|
import type { User, UserCreate } from '$lib/types/user.type';
|
||||||
import { preventDefault } from '$lib/utils/event-util';
|
import { preventDefault } from '$lib/utils/event-util';
|
||||||
import { createForm } from '$lib/utils/form-util';
|
import { createForm } from '$lib/utils/form-util';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
callback,
|
callback,
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
.min(2)
|
.min(2)
|
||||||
.max(30)
|
.max(30)
|
||||||
.regex(/^[a-z0-9_@.-]+$/, m.username_can_only_contain()),
|
.regex(/^[a-z0-9_@.-]+$/, m.username_can_only_contain()),
|
||||||
email: z.string().email(),
|
email: z.email(),
|
||||||
isAdmin: z.boolean(),
|
isAdmin: z.boolean(),
|
||||||
disabled: z.boolean()
|
disabled: z.boolean()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,11 +57,20 @@ test("Change Locale", async ({ page }) => {
|
|||||||
// Check if th language heading now says 'Taal' instead of 'Language'
|
// Check if th language heading now says 'Taal' instead of 'Language'
|
||||||
await expect(page.getByText("Taal", { exact: true })).toBeVisible();
|
await expect(page.getByText("Taal", { exact: true })).toBeVisible();
|
||||||
|
|
||||||
|
// Check if the validation messages are translated because they are provided by Zod
|
||||||
|
await page.getByRole("textbox", { name: "Voornaam" }).fill("");
|
||||||
|
await page.getByRole("button", { name: "Opslaan" }).click();
|
||||||
|
await expect(page.getByText("Te kort: verwacht dat string")).toBeVisible();
|
||||||
|
|
||||||
// Clear all cookies and sign in again to check if the language is still set to Dutch
|
// Clear all cookies and sign in again to check if the language is still set to Dutch
|
||||||
await page.context().clearCookies();
|
await page.context().clearCookies();
|
||||||
await authUtil.authenticate(page);
|
await authUtil.authenticate(page);
|
||||||
|
|
||||||
await expect(page.getByText("Taal", { exact: true })).toBeVisible();
|
await expect(page.getByText("Taal", { exact: true })).toBeVisible();
|
||||||
|
|
||||||
|
await page.getByRole("textbox", { name: "Voornaam" }).fill("");
|
||||||
|
await page.getByRole("button", { name: "Opslaan" }).click();
|
||||||
|
await expect(page.getByText("Te kort: verwacht dat string")).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Add passkey to an account", async ({ page }) => {
|
test("Add passkey to an account", async ({ page }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user