diff --git a/components.d.ts b/components.d.ts index 11710462..044d3888 100644 --- a/components.d.ts +++ b/components.d.ts @@ -69,6 +69,8 @@ declare module '@vue/runtime-core' { HtmlWysiwygEditor: typeof import('./src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue')['default'] HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default'] 'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default'] + 'IconMdi:contentCopy': typeof import('~icons/mdi/content-copy')['default'] + 'IconMdi:copy': typeof import('~icons/mdi/copy')['default'] 'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default'] IconMdiArrowRightBottom: typeof import('~icons/mdi/arrow-right-bottom')['default'] IconMdiCamera: typeof import('~icons/mdi/camera')['default'] @@ -170,6 +172,7 @@ declare module '@vue/runtime-core' { SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default'] SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default'] SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default'] + StringObfuscator: typeof import('./src/tools/string-obfuscator/string-obfuscator.vue')['default'] SvgPlaceholderGenerator: typeof import('./src/tools/svg-placeholder-generator/svg-placeholder-generator.vue')['default'] TemperatureConverter: typeof import('./src/tools/temperature-converter/temperature-converter.vue')['default'] TextareaCopyable: typeof import('./src/components/TextareaCopyable.vue')['default'] diff --git a/src/tools/index.ts b/src/tools/index.ts index 0cfc1739..c5686a48 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,6 +1,7 @@ import { tool as base64FileConverter } from './base64-file-converter'; import { tool as base64StringConverter } from './base64-string-converter'; import { tool as basicAuthGenerator } from './basic-auth-generator'; +import { tool as stringObfuscator } from './string-obfuscator'; import { tool as emojiPicker } from './emoji-picker'; import { tool as passwordStrengthAnalyser } from './password-strength-analyser'; import { tool as yamlToToml } from './yaml-to-toml'; @@ -145,7 +146,7 @@ export const toolsByCategory: ToolCategory[] = [ }, { name: 'Text', - components: [loremIpsumGenerator, textStatistics, emojiPicker], + components: [loremIpsumGenerator, textStatistics, emojiPicker, stringObfuscator], }, { name: 'Data', diff --git a/src/tools/string-obfuscator/index.ts b/src/tools/string-obfuscator/index.ts new file mode 100644 index 00000000..d5b45318 --- /dev/null +++ b/src/tools/string-obfuscator/index.ts @@ -0,0 +1,12 @@ +import { EyeOff } from '@vicons/tabler'; +import { defineTool } from '../tool'; + +export const tool = defineTool({ + name: 'String obfuscator', + path: '/string-obfuscator', + description: 'Obfuscate a string (like a secret, an IBAN, or a token) to make it shareable and identifiable without revealing its content.', + keywords: ['string', 'obfuscator', 'secret', 'token', 'hide', 'obscure', 'mask', 'masking'], + component: () => import('./string-obfuscator.vue'), + icon: EyeOff, + createdAt: new Date('2023-08-16'), +}); diff --git a/src/tools/string-obfuscator/string-obfuscator.model.test.ts b/src/tools/string-obfuscator/string-obfuscator.model.test.ts new file mode 100644 index 00000000..08d3fc24 --- /dev/null +++ b/src/tools/string-obfuscator/string-obfuscator.model.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from 'vitest'; +import { obfuscateString } from './string-obfuscator.model'; + +describe('string-obfuscator model', () => { + describe('obfuscateString', () => { + it('the characters in the middle of the string are replaced by the replacement character', () => { + expect(obfuscateString('1234567890')).toBe('1234******'); + expect(obfuscateString('1234567890', { replacementChar: 'x' })).toBe('1234xxxxxx'); + expect(obfuscateString('1234567890', { keepFirst: 5 })).toBe('12345*****'); + expect(obfuscateString('1234567890', { keepFirst: 0, keepLast: 5 })).toBe('*****67890'); + expect(obfuscateString('1234567890', { keepFirst: 5, keepLast: 5 })).toBe('1234567890'); + expect(obfuscateString('1234567890', { keepFirst: 2, keepLast: 2, replacementChar: 'x' })).toBe('12xxxxxx90'); + }); + + it('by default, the spaces are kept, they can be removed with the keepSpace option', () => { + expect(obfuscateString('12345 67890')).toBe('1234* *****'); + expect(obfuscateString('12345 67890', { keepSpace: false })).toBe('1234*******'); + }); + }); +}); diff --git a/src/tools/string-obfuscator/string-obfuscator.model.ts b/src/tools/string-obfuscator/string-obfuscator.model.ts new file mode 100644 index 00000000..7f56dd1e --- /dev/null +++ b/src/tools/string-obfuscator/string-obfuscator.model.ts @@ -0,0 +1,35 @@ +import { get } from '@vueuse/core'; +import { type MaybeRef, computed } from 'vue'; + +export { obfuscateString, useObfuscateString }; + +function obfuscateString( + str: string, + { replacementChar = '*', keepFirst = 4, keepLast = 0, keepSpace = true }: { replacementChar?: string; keepFirst?: number; keepLast?: number; keepSpace?: boolean } = {}): string { + return str + .split('') + .map((char, index, array) => { + if (keepSpace && char === ' ') { + return char; + } + + return (index < keepFirst || index >= array.length - keepLast) ? char : replacementChar; + }) + .join(''); +} + +function useObfuscateString( + str: MaybeRef, + config: { replacementChar?: MaybeRef; keepFirst?: MaybeRef; keepLast?: MaybeRef; keepSpace?: MaybeRef } = {}, + +) { + return computed(() => obfuscateString( + get(str), + { + replacementChar: get(config.replacementChar), + keepFirst: get(config.keepFirst), + keepLast: get(config.keepLast), + keepSpace: get(config.keepSpace), + }, + )); +} diff --git a/src/tools/string-obfuscator/string-obfuscator.vue b/src/tools/string-obfuscator/string-obfuscator.vue new file mode 100644 index 00000000..0f11fca1 --- /dev/null +++ b/src/tools/string-obfuscator/string-obfuscator.vue @@ -0,0 +1,47 @@ + + +