New:Socket for Asana Is Now Available.Learn more
Get Started

@isoftdata/svelte-password-fields

Package Overview
Dependencies
Maintainers
11
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@isoftdata/svelte-password-fields - npm Package Compare versions

Comparing version
1.5.0
to
2.0.0
+216
-147
dist/PasswordFields.svelte

@@ -1,146 +0,212 @@

<script>import Input from "@isoftdata/svelte-input";
import Button from "@isoftdata/svelte-button";
import { getContext } from "svelte";
const i18next = getContext("i18next");
const defaultTranslate = (_key, defaultString, replacementTokens) => {
if (replacementTokens && typeof replacementTokens === "object" && Object.keys(replacementTokens).length) {
return Object.entries(replacementTokens).reduce((acc, [key, value]) => {
return acc.replace(new RegExp(`{{${key}}}`, "g"), value.toString());
}, defaultString);
} else {
return defaultString;
}
};
const translate = typeof i18next?.t === "function" ? i18next.t : defaultTranslate;
const getUppercaseCharactersCount = (password2) => (password2.match(/[A-Z]/g) ?? []).length;
const getLowercaseCharactersCount = (password2) => (password2.match(/[a-z]/g) ?? []).length;
const getNumericCharacterCount = (password2) => (password2.match(/[0-9]/g) ?? []).length;
const getSpecialCharactersCount = (password2) => {
const uppercaseCharactersCount = getUppercaseCharactersCount(password2);
const lowercaseCharactersCount = getLowercaseCharactersCount(password2);
const numericCharactersCount = getNumericCharacterCount(password2);
return password2.length - uppercaseCharactersCount - lowercaseCharactersCount - numericCharactersCount;
};
const validationEnforcementMap = /* @__PURE__ */ new Map([
[
"minimumPasswordLength",
{
translationKey: "passwordFields:rules.numberOfRequiredCharacters.lowercase",
defaultTranslation: `Password must be at least {{minimumPasswordLength}} characters long`,
isValid: (password2, { minimumPasswordLength }) => {
return !!(password2 && isNumber(minimumPasswordLength) && password2.length >= minimumPasswordLength);
}
}
],
[
"maximumPasswordLength",
{
translationKey: "passwordFields:rules.numberOfRequiredCharacters.uppercase",
defaultTranslation: `Password must be no more than {{maximumPasswordLength}} characters long`,
isValid: (password2, { maximumPasswordLength }) => {
return !!(password2 && isNumber(maximumPasswordLength) && password2.length <= maximumPasswordLength);
}
}
],
[
"numberOfRequiredLowercaseCharacters",
{
translationKey: "passwordFields:rules.numberOfRequiredCharacters.numeric",
defaultTranslation: `Password must contain at least {{numberOfRequiredLowercaseCharacters}} lowercase characters`,
isValid: (password2, { numberOfRequiredLowercaseCharacters }) => {
return !!(password2 && isNumber(numberOfRequiredLowercaseCharacters) && getLowercaseCharactersCount(password2) >= numberOfRequiredLowercaseCharacters);
}
}
],
[
"numberOfRequiredUppercaseCharacters",
{
translationKey: "passwordFields:rules.numberOfRequiredCharacters.special",
defaultTranslation: `Password must contain at least {{numberOfRequiredUppercaseCharacters}} uppercase characters`,
isValid: (password2, { numberOfRequiredUppercaseCharacters }) => {
return !!(password2 && isNumber(numberOfRequiredUppercaseCharacters) && getUppercaseCharactersCount(password2) >= numberOfRequiredUppercaseCharacters);
}
}
],
[
"numberOfRequiredNumericCharacters",
{
translationKey: "passwordFields:rules.minimumLength",
defaultTranslation: `Password must contain at least {{numberOfRequiredNumericCharacters}} numeric characters`,
isValid: (password2, { numberOfRequiredNumericCharacters }) => {
return !!(password2 && isNumber(numberOfRequiredNumericCharacters) && getNumericCharacterCount(password2) >= numberOfRequiredNumericCharacters);
}
}
],
[
"numberOfRequiredSpecialCharacters",
{
translationKey: "passwordFields:rules.maximumLength",
defaultTranslation: `Password must contain at least {{numberOfRequiredSpecialCharacters}} special characters`,
isValid: (password2, { numberOfRequiredSpecialCharacters }) => {
return !!(password2 && isNumber(numberOfRequiredSpecialCharacters) && getSpecialCharactersCount(password2) >= numberOfRequiredSpecialCharacters);
}
}
]
]);
export let password = "";
export let confirmPassword = "";
export let columnClass = "col-12 col-md-6";
export let passwordLabel = translate("passwordFields:passwordLabel", "Password");
export let confirmPasswordLabel = translate("passwordFields:confirmPasswordLabel", "Confirm Password");
export let passwordMismatch = false;
export let validationRules = void 0;
export let showPasswordRules = false;
export let passwordValidationFailed = false;
export let passwordInput = void 0;
export let passwordConfirmInput = void 0;
export let passwordAutocomplete = void 0;
export let confirmPasswordAutocomplete = void 0;
export let passwordIsValid = false;
function checkForPasswordMismatch() {
if (password && confirmPassword && password !== confirmPassword) {
passwordMismatch = true;
} else if (password === confirmPassword) {
passwordMismatch = false;
}
}
const isNumber = (v) => typeof v === "number";
const isDefined = (v) => typeof v !== "undefined";
let enforcedValidationMessages = getEnforcedValidationMessages(validationRules, validationEnforcementMap);
function getEnforcedValidationMessages(validationRules2, validationEnforcementMap2) {
let validationMessages = [];
passwordValidationFailed = false;
if (isDefined(validationRules2)) {
const validationKeys = Object.keys(validationRules2);
validationKeys.forEach((key) => {
const { translationKey, defaultTranslation, isValid } = validationEnforcementMap2.get(key);
const passwordIsValid2 = isValid(password, validationRules2);
if (!passwordIsValid2) {
passwordValidationFailed = true;
}
const validationRulesValue = validationRules2[key];
const translationReplacement = { [key]: validationRulesValue };
validationMessages.push({
message: translate(translationKey, defaultTranslation, translationReplacement),
isValid: passwordIsValid2
});
});
}
return validationMessages;
}
$:
passwordIsValid = validationRules ? enforcedValidationMessages.every(({ isValid }) => isValid) : true;
<script lang="ts">
import type { i18n } from 'i18next'
import type { HTMLInputAttributes } from 'svelte/elements'
import type { ValidationRules } from './'
import Input from '@isoftdata/svelte-input'
import Button from '@isoftdata/svelte-button'
import Icon from '@isoftdata/svelte-icon'
import { getContext } from 'svelte'
import { translate as defaultTranslate } from '@isoftdata/utility-string'
const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
const getUppercaseCharactersCount = (password: string) => (password.match(/[A-Z]/g) ?? []).length
const getLowercaseCharactersCount = (password: string) => (password.match(/[a-z]/g) ?? []).length
const getNumericCharacterCount = (password: string) => (password.match(/[0-9]/g) ?? []).length
const getSpecialCharactersCount = (password: string) => {
const uppercaseCharactersCount = getUppercaseCharactersCount(password)
const lowercaseCharactersCount = getLowercaseCharactersCount(password)
const numericCharactersCount = getNumericCharacterCount(password)
return password.length - uppercaseCharactersCount - lowercaseCharactersCount - numericCharactersCount
}
interface ValidationRulesMapValue {
translationKey: string
defaultTranslation: string
isValid: (password: string, validationEnforcementMap: ValidationRules) => boolean
}
type ValidationEnforementMap = Map<keyof ValidationRules, ValidationRulesMapValue>
const validationEnforcementMap: ValidationEnforementMap = new Map([
[
'minimumPasswordLength',
{
translationKey: 'passwordFields:rules.numberOfRequiredCharacters.lowercase',
defaultTranslation: `Password must be at least {{minimumPasswordLength}} characters long`,
isValid: (password: string, { minimumPasswordLength }: ValidationRules): boolean => {
return !!(password && isNumber(minimumPasswordLength) && password.length >= minimumPasswordLength)
},
},
],
[
'maximumPasswordLength',
{
translationKey: 'passwordFields:rules.numberOfRequiredCharacters.uppercase',
defaultTranslation: `Password must be no more than {{maximumPasswordLength}} characters long`,
isValid: (password: string, { maximumPasswordLength }: ValidationRules): boolean => {
return !!(password && isNumber(maximumPasswordLength) && password.length <= maximumPasswordLength)
},
},
],
[
'numberOfRequiredLowercaseCharacters',
{
translationKey: 'passwordFields:rules.numberOfRequiredCharacters.numeric',
defaultTranslation: `Password must contain at least {{numberOfRequiredLowercaseCharacters}} lowercase characters`,
isValid: (password: string, { numberOfRequiredLowercaseCharacters }: ValidationRules): boolean => {
return !!(
password &&
isNumber(numberOfRequiredLowercaseCharacters) &&
getLowercaseCharactersCount(password) >= numberOfRequiredLowercaseCharacters
)
},
},
],
[
'numberOfRequiredUppercaseCharacters',
{
translationKey: 'passwordFields:rules.numberOfRequiredCharacters.special',
defaultTranslation: `Password must contain at least {{numberOfRequiredUppercaseCharacters}} uppercase characters`,
isValid: (password: string, { numberOfRequiredUppercaseCharacters }: ValidationRules): boolean => {
return !!(
password &&
isNumber(numberOfRequiredUppercaseCharacters) &&
getUppercaseCharactersCount(password) >= numberOfRequiredUppercaseCharacters
)
},
},
],
[
'numberOfRequiredNumericCharacters',
{
translationKey: 'passwordFields:rules.minimumLength',
defaultTranslation: `Password must contain at least {{numberOfRequiredNumericCharacters}} numeric characters`,
isValid: (password: string, { numberOfRequiredNumericCharacters }: ValidationRules): boolean => {
return !!(
password &&
isNumber(numberOfRequiredNumericCharacters) &&
getNumericCharacterCount(password) >= numberOfRequiredNumericCharacters
)
},
},
],
[
'numberOfRequiredSpecialCharacters',
{
translationKey: 'passwordFields:rules.maximumLength',
defaultTranslation: `Password must contain at least {{numberOfRequiredSpecialCharacters}} special characters`,
isValid: (password: string, { numberOfRequiredSpecialCharacters }: ValidationRules): boolean => {
return !!(
password &&
isNumber(numberOfRequiredSpecialCharacters) &&
getSpecialCharactersCount(password) >= numberOfRequiredSpecialCharacters
)
},
},
],
])
interface Props {
password?: string
confirmPassword?: string
columnClass?: string
passwordLabel?: string
confirmPasswordLabel?: string
passwordMismatch?: boolean
validationRules?: ValidationRules | undefined
showPasswordRules?: boolean
passwordValidationFailed?: boolean
passwordInput?: HTMLInputElement | undefined
passwordConfirmInput?: HTMLInputElement | undefined
passwordAutocomplete?: HTMLInputAttributes['autocomplete'] | undefined
confirmPasswordAutocomplete?: HTMLInputAttributes['autocomplete'] | undefined
passwordIsValid?: boolean
}
let {
password = $bindable(''),
confirmPassword = $bindable(''),
columnClass = 'col-12 col-md-6',
passwordLabel = translate('passwordFields:passwordLabel', 'Password'),
confirmPasswordLabel = translate('passwordFields:confirmPasswordLabel', 'Confirm Password'),
passwordMismatch = $bindable(false),
validationRules = undefined,
showPasswordRules = $bindable(false),
passwordValidationFailed = $bindable(false),
passwordInput = $bindable(undefined),
passwordConfirmInput = $bindable(undefined),
passwordAutocomplete = undefined,
confirmPasswordAutocomplete = undefined,
passwordIsValid = $bindable(false),
}: Props = $props()
function checkForPasswordMismatch() {
if (password && confirmPassword && password !== confirmPassword) {
passwordMismatch = true
} else if (password === confirmPassword) {
passwordMismatch = false
}
}
const isNumber = (v: unknown): v is number => typeof v === 'number'
const isDefined = (v: ValidationRules | undefined): v is ValidationRules => typeof v !== 'undefined'
type ValidationRulesKeys = keyof ValidationRules
interface ValidationMessage {
message: string
isValid: boolean
}
// this handles the initial load of the component
let enforcedValidationMessages: ValidationMessage[] = $state(
getEnforcedValidationMessages(validationRules, validationEnforcementMap),
)
function getEnforcedValidationMessages(
validationRules: ValidationRules | undefined,
validationEnforcementMap: ValidationEnforementMap,
): ValidationMessage[] {
let validationMessages: ValidationMessage[] = []
passwordValidationFailed = false
if (isDefined(validationRules)) {
const validationKeys = Object.keys(validationRules) as ValidationRulesKeys[]
validationKeys.forEach(key => {
const { translationKey, defaultTranslation, isValid } = validationEnforcementMap.get(
key,
) as ValidationRulesMapValue
const passwordIsValid = isValid(password, validationRules)
if (!passwordIsValid) {
passwordValidationFailed = true
}
const validationRulesValue = validationRules[key]
const translationReplacement = { [key]: validationRulesValue } as Record<string, string | number>
validationMessages.push({
message: translate(translationKey, defaultTranslation, translationReplacement),
isValid: passwordIsValid,
})
})
}
return validationMessages
}
$effect(() => {
passwordIsValid = validationRules ? enforcedValidationMessages.every(({ isValid }) => isValid) : true
})
</script>
<div class="form-row">
<div class="row">
<div class={columnClass}>
<Input
bind:input={passwordInput}
class={passwordMismatch ? 'is-invalid' : ''}
bind:value={password}
class={{ 'is-invalid': passwordMismatch }}
label={passwordLabel}
bind:value={password}
autocomplete={passwordAutocomplete}
type="password"
required
on:change={() => {
onchange={() => {
checkForPasswordMismatch()

@@ -151,3 +217,5 @@ if (passwordValidationFailed) {

}}
on:input={() => (enforcedValidationMessages = getEnforcedValidationMessages(validationRules, validationEnforcementMap))}
oninput={() => {
enforcedValidationMessages = getEnforcedValidationMessages(validationRules, validationEnforcementMap)
}}
/>

@@ -157,7 +225,7 @@ {#if validationRules}

color="link"
tabIndex="-1"
tabindex={-1}
size="xs"
class="pl-0"
textClass="small"
on:click={() => (showPasswordRules = !showPasswordRules)}>Password Rules</Button
onclick={() => (showPasswordRules = !showPasswordRules)}>Password Rules</Button
>

@@ -175,3 +243,3 @@ {/if}

required
on:change={checkForPasswordMismatch}
onchange={checkForPasswordMismatch}
/>

@@ -184,9 +252,10 @@ </div>

<li>
<i
class="fas fa-fw"
class:fa-check={isValid}
class:fa-xmark={!isValid}
class:text-success={isValid}
class:text-danger={!isValid}
></i>
<Icon
icon={isValid ? 'check' : 'xmark'}
fixedWidth
class={{
'text-success': isValid,
'text-danger': !isValid,
}}
/>
{message}

@@ -193,0 +262,0 @@ </li>

@@ -1,31 +0,21 @@

import { SvelteComponent } from "svelte";
import type { HTMLInputAttributes } from 'svelte/elements';
import type { ValidationRules } from './';
declare const __propDef: {
props: {
password?: string | undefined;
confirmPassword?: string | undefined;
columnClass?: string | undefined;
passwordLabel?: string | undefined;
confirmPasswordLabel?: string | undefined;
passwordMismatch?: boolean | undefined;
validationRules?: ValidationRules | undefined;
showPasswordRules?: boolean | undefined;
passwordValidationFailed?: boolean | undefined;
passwordInput?: HTMLInputElement | undefined;
passwordConfirmInput?: HTMLInputElement | undefined;
passwordAutocomplete?: HTMLInputAttributes['autocomplete'] | undefined;
confirmPasswordAutocomplete?: HTMLInputAttributes['autocomplete'] | undefined;
passwordIsValid?: boolean | undefined;
};
events: {
[evt: string]: CustomEvent<any>;
};
slots: {};
};
export type PasswordFieldsProps = typeof __propDef.props;
export type PasswordFieldsEvents = typeof __propDef.events;
export type PasswordFieldsSlots = typeof __propDef.slots;
export default class PasswordFields extends SvelteComponent<PasswordFieldsProps, PasswordFieldsEvents, PasswordFieldsSlots> {
interface Props {
password?: string;
confirmPassword?: string;
columnClass?: string;
passwordLabel?: string;
confirmPasswordLabel?: string;
passwordMismatch?: boolean;
validationRules?: ValidationRules | undefined;
showPasswordRules?: boolean;
passwordValidationFailed?: boolean;
passwordInput?: HTMLInputElement | undefined;
passwordConfirmInput?: HTMLInputElement | undefined;
passwordAutocomplete?: HTMLInputAttributes['autocomplete'] | undefined;
confirmPasswordAutocomplete?: HTMLInputAttributes['autocomplete'] | undefined;
passwordIsValid?: boolean;
}
export {};
declare const PasswordFields: import("svelte").Component<Props, {}, "password" | "confirmPassword" | "passwordMismatch" | "showPasswordRules" | "passwordValidationFailed" | "passwordInput" | "passwordConfirmInput" | "passwordIsValid">;
type PasswordFields = ReturnType<typeof PasswordFields>;
export default PasswordFields;
{
"name": "@isoftdata/svelte-password-fields",
"version": "1.5.0",
"scripts": {
"dev": "vite dev",
"build": "vite build && npm run package",
"preview": "vite preview",
"package": "svelte-kit sync && svelte-package && publint",
"prepublishOnly": "npm run package",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write ."
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
}
},
"files": [
"dist",
"!dist/**/*.test.*",
"!dist/**/*.spec.*"
],
"peerDependencies": {
"svelte": "^4.0.0 || ^5.1.6"
},
"devDependencies": {
"@isoftdata/prettier-config": "^1.0.3",
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.27.4",
"@sveltejs/package": "^2.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.28.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-svelte": "^2.30.0",
"i18next": "^23.7.7",
"prettier": "^3.0.0",
"prettier-plugin-svelte": "^3.0.0",
"publint": "^0.1.9",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.4.2"
},
"svelte": "./dist/index.js",
"types": "./dist/index.d.ts",
"type": "module",
"prettier": "@isoftdata/prettier-config",
"dependencies": {
"@isoftdata/svelte-button": "^1.3.1",
"@isoftdata/svelte-input": "^1.2.1"
}
}
"name": "@isoftdata/svelte-password-fields",
"version": "2.0.0",
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
}
},
"files": [
"dist",
"!dist/**/*.test.*",
"!dist/**/*.spec.*"
],
"peerDependencies": {
"svelte": "^5.23.2"
},
"devDependencies": {
"@isoftdata/prettier-config": "^2.0.4",
"@isoftdata/svelte-bootstrap-version-switcher": "^1.1.2",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/kit": "^2.20.8",
"@sveltejs/package": "^2.3.11",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.46.1",
"i18next": "^23.16.8",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.3.3",
"publint": "^0.1.16",
"svelte": "^5.0.0",
"svelte-check": "^4.1.7",
"tslib": "^2.8.1",
"typescript": "^5.8.3",
"vite": "^6.3.4"
},
"svelte": "./dist/index.js",
"types": "./dist/index.d.ts",
"type": "module",
"prettier": "@isoftdata/prettier-config",
"dependencies": {
"@isoftdata/svelte-button": "^2.1.0",
"@isoftdata/svelte-icon": "^2.0.1",
"@isoftdata/svelte-input": "^2.0.1",
"@isoftdata/utility-string": "^2.1.0",
"svelte": "^5.28.2"
},
"engines": {
"pnpm": "10.x"
},
"scripts": {
"dev": "vite dev",
"build": "vite build && npm run package",
"preview": "vite preview",
"package": "svelte-kit sync && svelte-package && publint",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write ."
}
}

@@ -8,5 +8,10 @@ # Svelte PasswordFields

```sh
npm i @isoftdata/svelte-password-fields
pnpm i @isoftdata/svelte-password-fields
```
## Breaking changes
### 2.0.0
- Require Svelte 5
## Props

@@ -13,0 +18,0 @@