diff --git a/jsrepo-build-config.json b/jsrepo-build-config.json index d3db800..12423ac 100644 --- a/jsrepo-build-config.json +++ b/jsrepo-build-config.json @@ -3,6 +3,8 @@ "dirs": ["./src/lib/components", "./src/lib"], "doNotListBlocks": [ "utils", + "ipv4-address", + "types", "is-mobile.svelte", "is-number", "badge", diff --git a/jsrepo-manifest.json b/jsrepo-manifest.json index 1e35113..6816cb6 100644 --- a/jsrepo-manifest.json +++ b/jsrepo-manifest.json @@ -174,12 +174,13 @@ "subdirectory": true, "list": true, "files": ["index.ts", "ipv4address-input-input.svelte", "ipv4address-input.svelte"], - "localDependencies": ["utils/is-number", "utils/utils"], + "localDependencies": ["utils/is-number", "utils/utils", "utils/ipv4-address"], "dependencies": [], "devDependencies": [], "_imports_": { "$lib/utils/is-number": "{{utils/is-number}}", - "$lib/utils/utils": "{{utils/utils}}" + "$lib/utils/utils": "{{utils/utils}}", + "$lib/utils/ipv4-address": "{{utils/ipv4-address}}" } }, { @@ -389,6 +390,22 @@ "dependencies": [], "devDependencies": [] }, + { + "name": "ipv4-address", + "directory": "src/lib/utils", + "category": "utils", + "tests": false, + "subdirectory": false, + "list": false, + "files": ["ipv4-address.ts"], + "localDependencies": ["utils/types", "utils/is-number"], + "_imports_": { + "$lib/utils/types/result": "{{utils/types}}/result", + "$lib/utils/is-number": "{{utils/is-number}}" + }, + "dependencies": [], + "devDependencies": [] + }, { "name": "is-number", "directory": "src/lib/utils", @@ -402,6 +419,19 @@ "dependencies": [], "devDependencies": [] }, + { + "name": "types", + "directory": "src/lib/utils/types", + "category": "utils", + "tests": false, + "subdirectory": true, + "list": false, + "files": ["result.ts"], + "localDependencies": [], + "dependencies": [], + "devDependencies": [], + "_imports_": {} + }, { "name": "utils", "directory": "src/lib/utils", diff --git a/jsrepo.json b/jsrepo.json index 80db64e..70a12f6 100644 --- a/jsrepo.json +++ b/jsrepo.json @@ -6,7 +6,7 @@ "formatter": "prettier", "paths": { "*": "./src/blocks", - "types": "$lib/ts/types", + "types": "$lib/utils/types", "utils": "$lib/utils", "ui": "$lib/components/ui", "hooks": "$lib/hooks" diff --git a/package.json b/package.json index 50abd33..b4bdff7 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "prettier-plugin-tailwindcss": "^0.6.5", "runed": "^0.18.0", "shiki": "^1.24.2", - "svelte": "^5.11.0", + "svelte": "^5.11.2", "svelte-check": "^4.0.0", "svelte-sonner": "^0.3.28", "svelte-tel-input": "^3.5.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa613f2..581683c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,19 +19,19 @@ importers: version: 5.1.0 '@sveltejs/adapter-vercel': specifier: ^5.5.2 - version: 5.5.2(@sveltejs/kit@2.10.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(rollup@4.28.0) + version: 5.5.2(@sveltejs/kit@2.10.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(rollup@4.28.0) '@sveltejs/kit': specifier: ^2.10.1 - version: 2.10.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) + version: 2.10.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) '@sveltejs/vite-plugin-svelte': specifier: ^5.0.0 - version: 5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) + version: 5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.49) bits-ui: specifier: 1.0.0-next.71 - version: 1.0.0-next.71(svelte@5.11.0) + version: 1.0.0-next.71(svelte@5.11.2) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -43,7 +43,7 @@ importers: version: 9.1.0(eslint@9.16.0(jiti@1.21.6)) eslint-plugin-svelte: specifier: ^2.45.1 - version: 2.46.1(eslint@9.16.0(jiti@1.21.6))(svelte@5.11.0) + version: 2.46.1(eslint@9.16.0(jiti@1.21.6))(svelte@5.11.2) globals: specifier: ^15.0.0 version: 15.13.0 @@ -52,37 +52,37 @@ importers: version: 1.19.1(typescript@5.7.2) lucide-svelte: specifier: ^0.468.0 - version: 0.468.0(svelte@5.11.0) + version: 0.468.0(svelte@5.11.2) mode-watcher: specifier: ^0.5.0 - version: 0.5.0(svelte@5.11.0) + version: 0.5.0(svelte@5.11.2) prettier: specifier: ^3.3.2 version: 3.4.2 prettier-plugin-svelte: specifier: ^3.2.6 - version: 3.3.2(prettier@3.4.2)(svelte@5.11.0) + version: 3.3.2(prettier@3.4.2)(svelte@5.11.2) prettier-plugin-tailwindcss: specifier: ^0.6.5 - version: 0.6.9(prettier-plugin-svelte@3.3.2(prettier@3.4.2)(svelte@5.11.0))(prettier@3.4.2) + version: 0.6.9(prettier-plugin-svelte@3.3.2(prettier@3.4.2)(svelte@5.11.2))(prettier@3.4.2) runed: specifier: ^0.18.0 - version: 0.18.0(svelte@5.11.0) + version: 0.18.0(svelte@5.11.2) shiki: specifier: ^1.24.2 version: 1.24.2 svelte: - specifier: ^5.11.0 - version: 5.11.0 + specifier: ^5.11.2 + version: 5.11.2 svelte-check: specifier: ^4.0.0 - version: 4.1.1(picomatch@4.0.2)(svelte@5.11.0)(typescript@5.7.2) + version: 4.1.1(picomatch@4.0.2)(svelte@5.11.2)(typescript@5.7.2) svelte-sonner: specifier: ^0.3.28 - version: 0.3.28(svelte@5.11.0) + version: 0.3.28(svelte@5.11.2) svelte-tel-input: specifier: ^3.5.2 - version: 3.5.2(svelte@5.11.0) + version: 3.5.2(svelte@5.11.2) tailwind-merge: specifier: ^2.5.5 version: 2.5.5 @@ -103,7 +103,7 @@ importers: version: 8.18.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) vaul-svelte: specifier: 1.0.0-next.3 - version: 1.0.0-next.3(svelte@5.11.0) + version: 1.0.0-next.3(svelte@5.11.2) vite: specifier: ^6.0.0 version: 6.0.3(jiti@1.21.6)(yaml@2.6.1) @@ -2023,8 +2023,8 @@ packages: peerDependencies: svelte: ^5.0.0-next.126 - svelte@5.11.0: - resolution: {integrity: sha512-w4FYvEY1eKbgBZo8RY2iegaOe9sZu9yhDa70cAyW9gkPJc87w6/1rrfNI4uu985s/7U+4CggQDE7CPIbrPsnXw==} + svelte@5.11.2: + resolution: {integrity: sha512-kGWswlBaohYxZHML9jp8ZYXkwjKd+WTpyAK1CCDmNzsefZHQjvsa7kbrKUckcFloNmdzwQwaZq+NyunuNOE6lw==} engines: {node: '>=18'} tailwind-merge@2.5.5: @@ -2777,9 +2777,9 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} - '@sveltejs/adapter-vercel@5.5.2(@sveltejs/kit@2.10.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(rollup@4.28.0)': + '@sveltejs/adapter-vercel@5.5.2(@sveltejs/kit@2.10.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(rollup@4.28.0)': dependencies: - '@sveltejs/kit': 2.10.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) + '@sveltejs/kit': 2.10.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) '@vercel/nft': 0.27.9(rollup@4.28.0) esbuild: 0.24.0 transitivePeerDependencies: @@ -2787,9 +2787,9 @@ snapshots: - rollup - supports-color - '@sveltejs/kit@2.10.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1))': + '@sveltejs/kit@2.10.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) + '@sveltejs/vite-plugin-svelte': 5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) '@types/cookie': 0.6.0 cookie: 0.6.0 devalue: 5.1.1 @@ -2801,27 +2801,27 @@ snapshots: sade: 1.8.1 set-cookie-parser: 2.7.1 sirv: 3.0.0 - svelte: 5.11.0 + svelte: 5.11.2 tiny-glob: 0.2.9 vite: 6.0.3(jiti@1.21.6)(yaml@2.6.1) - '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1))': + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) + '@sveltejs/vite-plugin-svelte': 5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) debug: 4.3.7 - svelte: 5.11.0 + svelte: 5.11.2 vite: 6.0.3(jiti@1.21.6)(yaml@2.6.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1))': + '@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.0)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.1(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)))(svelte@5.11.2)(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) debug: 4.3.7 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.14 - svelte: 5.11.0 + svelte: 5.11.2 vite: 6.0.3(jiti@1.21.6)(yaml@2.6.1) vitefu: 1.0.4(vite@6.0.3(jiti@1.21.6)(yaml@2.6.1)) transitivePeerDependencies: @@ -3073,15 +3073,15 @@ snapshots: dependencies: file-uri-to-path: 1.0.0 - bits-ui@1.0.0-next.71(svelte@5.11.0): + bits-ui@1.0.0-next.71(svelte@5.11.2): dependencies: '@floating-ui/core': 1.6.8 '@floating-ui/dom': 1.6.12 '@internationalized/date': 3.6.0 esm-env: 1.2.1 - runed: 0.15.4(svelte@5.11.0) - svelte: 5.11.0 - svelte-toolbelt: 0.4.6(svelte@5.11.0) + runed: 0.15.4(svelte@5.11.2) + svelte: 5.11.2 + svelte-toolbelt: 0.4.6(svelte@5.11.2) bottleneck@2.19.5: {} @@ -3274,7 +3274,7 @@ snapshots: dependencies: eslint: 9.16.0(jiti@1.21.6) - eslint-plugin-svelte@2.46.1(eslint@9.16.0(jiti@1.21.6))(svelte@5.11.0): + eslint-plugin-svelte@2.46.1(eslint@9.16.0(jiti@1.21.6))(svelte@5.11.2): dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.16.0(jiti@1.21.6)) '@jridgewell/sourcemap-codec': 1.5.0 @@ -3287,9 +3287,9 @@ snapshots: postcss-safe-parser: 6.0.0(postcss@8.4.49) postcss-selector-parser: 6.1.2 semver: 7.6.3 - svelte-eslint-parser: 0.43.0(svelte@5.11.0) + svelte-eslint-parser: 0.43.0(svelte@5.11.2) optionalDependencies: - svelte: 5.11.0 + svelte: 5.11.2 transitivePeerDependencies: - ts-node @@ -3647,7 +3647,7 @@ snapshots: package-manager-detector: 0.2.7 pathe: 1.1.2 prettier: 3.4.2 - svelte: 5.11.0 + svelte: 5.11.2 ts-morph: 24.0.0 valibot: 0.42.1(typescript@5.7.2) validate-npm-package-name: 6.0.0 @@ -3687,9 +3687,9 @@ snapshots: lru-cache@10.4.3: {} - lucide-svelte@0.468.0(svelte@5.11.0): + lucide-svelte@0.468.0(svelte@5.11.2): dependencies: - svelte: 5.11.0 + svelte: 5.11.2 magic-string@0.30.14: dependencies: @@ -3750,9 +3750,9 @@ snapshots: mkdirp@3.0.1: {} - mode-watcher@0.5.0(svelte@5.11.0): + mode-watcher@0.5.0(svelte@5.11.2): dependencies: - svelte: 5.11.0 + svelte: 5.11.2 mri@1.2.0: {} @@ -3936,16 +3936,16 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-svelte@3.3.2(prettier@3.4.2)(svelte@5.11.0): + prettier-plugin-svelte@3.3.2(prettier@3.4.2)(svelte@5.11.2): dependencies: prettier: 3.4.2 - svelte: 5.11.0 + svelte: 5.11.2 - prettier-plugin-tailwindcss@0.6.9(prettier-plugin-svelte@3.3.2(prettier@3.4.2)(svelte@5.11.0))(prettier@3.4.2): + prettier-plugin-tailwindcss@0.6.9(prettier-plugin-svelte@3.3.2(prettier@3.4.2)(svelte@5.11.2))(prettier@3.4.2): dependencies: prettier: 3.4.2 optionalDependencies: - prettier-plugin-svelte: 3.3.2(prettier@3.4.2)(svelte@5.11.0) + prettier-plugin-svelte: 3.3.2(prettier@3.4.2)(svelte@5.11.2) prettier@3.4.2: {} @@ -4027,15 +4027,15 @@ snapshots: dependencies: queue-microtask: 1.2.3 - runed@0.15.4(svelte@5.11.0): + runed@0.15.4(svelte@5.11.2): dependencies: esm-env: 1.2.1 - svelte: 5.11.0 + svelte: 5.11.2 - runed@0.18.0(svelte@5.11.0): + runed@0.18.0(svelte@5.11.2): dependencies: esm-env: 1.2.1 - svelte: 5.11.0 + svelte: 5.11.2 sade@1.8.1: dependencies: @@ -4125,19 +4125,19 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.1.1(picomatch@4.0.2)(svelte@5.11.0)(typescript@5.7.2): + svelte-check@4.1.1(picomatch@4.0.2)(svelte@5.11.2)(typescript@5.7.2): dependencies: '@jridgewell/trace-mapping': 0.3.25 chokidar: 4.0.1 fdir: 6.4.2(picomatch@4.0.2) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.11.0 + svelte: 5.11.2 typescript: 5.7.2 transitivePeerDependencies: - picomatch - svelte-eslint-parser@0.43.0(svelte@5.11.0): + svelte-eslint-parser@0.43.0(svelte@5.11.2): dependencies: eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 @@ -4145,24 +4145,24 @@ snapshots: postcss: 8.4.49 postcss-scss: 4.0.9(postcss@8.4.49) optionalDependencies: - svelte: 5.11.0 + svelte: 5.11.2 - svelte-sonner@0.3.28(svelte@5.11.0): + svelte-sonner@0.3.28(svelte@5.11.2): dependencies: - svelte: 5.11.0 + svelte: 5.11.2 - svelte-tel-input@3.5.2(svelte@5.11.0): + svelte-tel-input@3.5.2(svelte@5.11.2): dependencies: libphonenumber-js: 1.10.43 - svelte: 5.11.0 + svelte: 5.11.2 - svelte-toolbelt@0.4.6(svelte@5.11.0): + svelte-toolbelt@0.4.6(svelte@5.11.2): dependencies: clsx: 2.1.1 style-to-object: 1.0.8 - svelte: 5.11.0 + svelte: 5.11.2 - svelte@5.11.0: + svelte@5.11.2: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.0 @@ -4335,11 +4335,11 @@ snapshots: validate-npm-package-name@6.0.0: {} - vaul-svelte@1.0.0-next.3(svelte@5.11.0): + vaul-svelte@1.0.0-next.3(svelte@5.11.2): dependencies: - bits-ui: 1.0.0-next.71(svelte@5.11.0) - svelte: 5.11.0 - svelte-toolbelt: 0.4.6(svelte@5.11.0) + bits-ui: 1.0.0-next.71(svelte@5.11.2) + svelte: 5.11.2 + svelte-toolbelt: 0.4.6(svelte@5.11.2) vfile-message@4.0.2: dependencies: diff --git a/src/lib/components/docs/examples/configure-device.svelte b/src/lib/components/docs/examples/configure-device.svelte index ea33bb1..945bedf 100644 --- a/src/lib/components/docs/examples/configure-device.svelte +++ b/src/lib/components/docs/examples/configure-device.svelte @@ -16,11 +16,11 @@
- +
- +
diff --git a/src/lib/components/ui/ipv4address-input/index.ts b/src/lib/components/ui/ipv4address-input/index.ts index 561b8d0..0aaa2a5 100644 --- a/src/lib/components/ui/ipv4address-input/index.ts +++ b/src/lib/components/ui/ipv4address-input/index.ts @@ -1,9 +1,4 @@ -/* - jsrepo 1.17.5 - Installed from github/ieedan/shadcn-ipv4address-input-svelte - 12-5-2024 -*/ - +import { isNumber } from '$lib/utils/is-number'; import IPv4AddressInput from './ipv4address-input.svelte'; /** Attempts to parse the provided address into a valid IP. Returns undefined for @@ -29,10 +24,24 @@ export const safeParseIPv4Address = ( segments.push(null); } + for (let i = 0; i < segments.length; i++) { + if (!isNumber(segments[i]) || segments[i] === null) { + segments[i] = null; + continue; + } + + const num = Number.parseInt(segments[i]!); + + if (num < 0 || num > 255) { + segments[i] = null; + continue; + } + + segments[i] = num.toString(); + } + // @ts-expect-error We know this is 4 we just made sure return segments; }; -export type IPv4Address = [number | null, number | null, number | null, number | null]; - export { IPv4AddressInput }; diff --git a/src/lib/components/ui/ipv4address-input/ipv4address-input-input.svelte b/src/lib/components/ui/ipv4address-input/ipv4address-input-input.svelte index b01b4cc..4bd8328 100644 --- a/src/lib/components/ui/ipv4address-input/ipv4address-input-input.svelte +++ b/src/lib/components/ui/ipv4address-input/ipv4address-input-input.svelte @@ -1,15 +1,9 @@ - -
secondInput?.focus()} - bind:value={value[0]} + bind:value={() => octets[0], + (v) => { + const tempOctets = octets; + + if (v == null || v === '') { + tempOctets[0] = null; + } else { + tempOctets[0] = v; + } + + value = format(tempOctets); + }} placeholder={parsedPlaceholder ? parsedPlaceholder[0] : undefined} onpaste={paste} /> @@ -79,7 +102,18 @@ bind:ref={secondInput} goNext={() => thirdInput?.focus()} goPrevious={() => firstInput?.focus()} - bind:value={value[1]} + bind:value={() => octets[1], + (v) => { + const tempOctets = octets; + + if (v == null || v === '') { + tempOctets[1] = null; + } else { + tempOctets[1] = v; + } + + value = format(tempOctets); + }} placeholder={parsedPlaceholder ? parsedPlaceholder[1] : undefined} onpaste={paste} /> @@ -88,7 +122,18 @@ bind:ref={thirdInput} goNext={() => fourthInput?.focus()} goPrevious={() => secondInput?.focus()} - bind:value={value[2]} + bind:value={() => octets[2], + (v) => { + const tempOctets = octets; + + if (v == null || v === '') { + tempOctets[2] = null; + } else { + tempOctets[2] = v; + } + + value = format(tempOctets); + }} placeholder={parsedPlaceholder ? parsedPlaceholder[2] : undefined} onpaste={paste} /> @@ -96,8 +141,20 @@ thirdInput?.focus()} - bind:value={value[3]} + bind:value={() => octets[3], + (v) => { + const tempOctets = octets; + + if (v == null || v === '') { + tempOctets[3] = null; + } else { + tempOctets[3] = v; + } + + value = format(tempOctets); + }} placeholder={parsedPlaceholder ? parsedPlaceholder[3] : undefined} onpaste={paste} />
+ diff --git a/src/lib/utils/ipv4-address.ts b/src/lib/utils/ipv4-address.ts new file mode 100644 index 0000000..91fbb75 --- /dev/null +++ b/src/lib/utils/ipv4-address.ts @@ -0,0 +1,114 @@ +/* + jsrepo 1.19.1 + Installed from github/ieedan/std + 12-11-2024 +*/ + +import { Err, Ok, type Result } from '$lib/utils/types/result'; +import { isNumber } from '$lib/utils/is-number'; + +type Octets = [number, number, number, number]; + +type IPv4Address = + | Octets + | `${number}.${number}.${number}.${number}` + | `${number} ${number} ${number} ${number}` + | `${number}_${number}_${number}_${number}`; + +type ParseError = { + octet?: number; + message: string; +}; + +/** Parses the ip address from a string in the format of `0.0.0.0` or `0 0 0 0` or `0_0_0_0` into an array of octets + * + * @param address + * @returns + * + * ## Usage + * + * ```ts + * parse("192.168.100.10").unwrap(); // [192, 168, 100, 10] + * ``` + */ +const parse = (address: string): Result => { + let newAddress = address.trim(); + + newAddress = newAddress.replaceAll(' ', '.'); + newAddress = newAddress.replaceAll('_', '.'); + + const octets = newAddress.split('.'); + + if (octets.length !== 4) + return Err({ message: `'${address}' is invalid as it doesn't contain 4 octets.` }); + + const final: Octets = [0, 0, 0, 0]; + + for (let i = 0; i < octets.length; i++) { + const octet = octets[i]; + + if (!isNumber(octet)) return Err({ octet: i + 1, message: `'${octet}' is not a number.` }); + + const num = Number.parseInt(octet); + + if (num < 0 || num > 255) return Err({ octet: i + 1, message: `'${octet}' is out of range.` }); + + final[i] = num; + } + + return Ok(final); +}; + +/** Validates the provided address + * + * @param address + * @returns + * + * ## Usage + * + * ```ts + * validate("192.168.100.10"); // true + * validate([192, 168, 100, 10]); // true + * + * validate("192.168.100.256"); // false + * validate([192, 168, 100, 256]); // false + * ``` + */ +const validate = (address: IPv4Address): boolean => { + if (typeof address === 'string') return parse(address).isOk(); + + for (let i = 0; i < address.length; i++) { + const octet = address[i]; + + if (octet < 0 || octet > 255) return false; + } + + return true; +}; + +/** Formats the provided address to a string with the provided separator + * + * @param address + * @param separator @default "." + * @returns + * + * ## Usage + * + * ```ts + * formatToString([192, 168, 100, 10]); // "192.168.100.10" + * ``` + */ +const formatToString = ( + address: IPv4Address, + separator: '.' | '_' | ' ' = '.' +): Result => { + if (Array.isArray(address)) return Ok(address.join(separator)); + + const parsed = parse(address); + + if (parsed.isErr()) return Err(parsed.unwrapErr().message); + + return formatToString(parsed.unwrap(), separator); +}; + +export { parse, validate, formatToString }; diff --git a/src/lib/utils/is-number.ts b/src/lib/utils/is-number.ts index a5d17f6..3968425 100644 --- a/src/lib/utils/is-number.ts +++ b/src/lib/utils/is-number.ts @@ -1,7 +1,7 @@ /* - jsrepo 1.17.5 + jsrepo 1.19.1 Installed from github/ieedan/std - 12-5-2024 + 12-11-2024 */ /** Checks if provided value is actually a number. diff --git a/src/lib/utils/types/result.ts b/src/lib/utils/types/result.ts new file mode 100644 index 0000000..90f1335 --- /dev/null +++ b/src/lib/utils/types/result.ts @@ -0,0 +1,736 @@ +/* + jsrepo 1.19.1 + Installed from github/ieedan/std + 12-11-2024 +*/ + +/** This is just a helper type used only within this file */ +type _Result = { ok: true; val: T } | { ok: false; err: E }; + +/** Result allows you to show to a consumer that a function might throw and force them to handle it. + * + * `T` Value type + * + * `E` Error type + * + * ## Usage + * + * ```ts + * function functionThatMightFail(): Result; + * ``` + * + * ## Examples + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello, World!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrap()); // "Hello, World!" + * ``` + */ +class Result { + private readonly _result: _Result; + + constructor(result: _Result) { + this._result = result; + } + + /** Allows you to run callbacks based on the result. + * + * @param success callback to be run when result is success + * @param failure callback to be run when result is failure + * @returns + * + * ## Usage + * + * ```ts + * result.match( + * (val) => val, + * () => { + * throw new Error('oops!') + * } + * ); + * ``` + * + * ## Examples + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello, World!"); + * + * const result = functionThatMightFail(); + * + * const val = result.match( + * (val) => val, + * () => { + * throw new Error('oops!') + * } + * ); + * + * console.log(val); // "Hello, World!" + * ``` + */ + match(success: (val: T) => A, failure: (err: E) => B): A | B { + if (!this._result.ok) { + return failure(this._result.err); + } + + return success(this._result.val); + } + + /** Maps `Result` to `Result` using the passed mapping function + * + * @param fn Mapping function + * @returns + * + * ## Usage + * + * ```ts + * result.map((val) => val.length); + * ``` + * + * ## Examples + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello, World!"); + * + * const result = functionThatMightFail(); + * + * const hello = result.map((val) => val.slice(0, 5)); + * + * console.log(hello.unwrap()); // "Hello" + * ``` + */ + map(fn: (val: T) => A): Result { + return this.match( + (val) => Ok(fn(val)), + (err) => Err(err) + ); + } + + /** In the `Ok` case returns the mapped value using the function else returns `defaultVal` + * + * @param defaultVal Value to be returned when `Err` + * @param fn Mapping function to map in case of `Ok` + * @returns + * + * ## Usage + * + * ```ts + * result.mapOr(1, (val) => val.length); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("foo"); + * + * const result = functionThatMightFail(); + * + * const length = result.mapOr(1, (val) => val.length); + * + * console.log(length); // 3 + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * const length = result.mapOr(1, (val) => val.length); + * + * console.log(length); // 1 + * ``` + */ + mapOr(defaultVal: A, fn: (val: T) => A): A { + return this.match( + (val) => fn(val), + (_) => defaultVal + ); + } + + /** In the `Ok` case returns the mapped value using `fn` else returns value of `def` + * + * @param def Mapping function called when `Err` + * @param fn Mapping function called when `Ok` + * @returns + * + * ## Usage + * + * ```ts + * result.mapOrElse(() => 1, (val) => val.length); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("foo"); + * + * const result = functionThatMightFail(); + * + * const length = result.mapOrElse(() => 1, (val) => val.length); + * + * console.log(length); // 3 + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * const length = result.mapOr(() => 1, (val) => val.length); + * + * console.log(length); // 1 + * ``` + */ + mapOrElse(def: (err: E) => A, fn: (val: T) => A): A { + return this.match( + (val) => fn(val), + (err) => def(err) + ); + } + + /** Maps `Result` to `Result` using the passed mapping function + * + * @param fn Mapping function + * @returns + * + * ## Usage + * + * ```ts + * result.mapErr((err) => getCodeMsg(err)); + * ``` + * + * ## Examples + * + * ```ts + * const functionThatMightFail = (): Result => Err(10); + * + * const result = functionThatMightFail(); + * + * const message = result.mapErr(() => "Error"); + * + * console.log(message); // "Error" + * ``` + */ + mapErr(fn: (err: E) => A): Result { + return this.match( + (val) => Ok(val), + (err) => Err(fn(err)) + ); + } + + /** In the `Err` case returns the mapped value using the function else returns `defaultVal` + * + * @param defaultVal Value to be returned when `Ok` + * @param fn Mapping function to map in case of `Err` + * @returns + * + * ## Usage + * + * ```ts + * result.mapErrOr("Should've been error", (err) => getCodeMsg(err)); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("foo"); + * + * const result = functionThatMightFail(); + * + * const message = result.mapErrOr("Should've been error", () => "Error"); + * + * console.log(message); // "Should've been error" + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err(10); + * + * const result = functionThatMightFail(); + * + * const message = result.mapErrOr("Should've been error", () => "Error"); + * + * console.log(message); // "Error" + * ``` + */ + mapErrOr(defaultVal: A, fn: (err: E) => A): A { + return this.match( + (_) => defaultVal, + (err) => fn(err) + ); + } + + /** In the `Err` case returns the mapped value using the function else returns value of `def` + * + * @param def Mapping function called when `Ok` + * @param fn Mapping function called when `Err` + * @returns + * + * ## Usage + * + * ```ts + * result.mapErrOrElse(() => "Value", (_) => "Error!"); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("foo"); + * + * const result = functionThatMightFail(); + * + * const length = result.mapErrOrElse(() => 1, (val) => val.length); + * + * console.log(length); // 1 + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * const length = result.mapOr(() => 1, (val) => val.length); + * + * console.log(length); // 4 + * ``` + */ + mapErrOrElse(def: (val: T) => A, fn: (err: E) => A): A { + return this.match( + (val) => def(val), + (err) => fn(err) + ); + } + + /** Returns true if result is `Ok` + * + * @returns + * + * ## Usage + * + * ```ts + * result.isOk(); + * ``` + */ + isOk(): boolean { + return this.match( + () => true, + () => false + ); + } + + /** Returns true if result is `Err` + * + * @returns + * + * ## Usage + * + * ```ts + * result.isErr(); + * ``` + */ + isErr(): boolean { + return this.match( + () => false, + () => true + ); + } + + /** Tries to return value if value is `Err` throws generic error message. + * + * @returns + * + * ## Usage + * + * ```ts + * result.unwrap(); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrap()); // "Hello!" + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * result.unwrap(); // Error: Attempted to call `.unwrap()` on a non `Ok` value. + * ``` + */ + unwrap(): T { + return this.match( + (val) => val, + () => { + throw new Error('Attempted to call `.unwrap()` on a non `Ok` value.'); + } + ); + } + + /** Tries to return err if value is `Ok` throws generic error message. + * + * @returns + * + * ## Usage + * + * ```ts + * result.unwrapErr(); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello!"); + * + * const result = functionThatMightFail(); + * + * result.unwrapErr(); // Error: Attempted to call `.unwrapErr()` on a non `Err` value. + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrapErr()); // "oops!" + * ``` + */ + unwrapErr(): E { + return this.match( + () => { + throw new Error('Attempted to call `.unwrapErr()` on a non `Err` value.'); + }, + (err) => err + ); + } + + /** Tries to unwrap the value if value is `Err` returns `defaultVal` + * + * @param defaultVal Value to be returned if `Err` + * @returns + * + * ## Usage + * + * ```ts + * result.unwrapOr(7); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrapOr("Yellow!")); // "Hello!" + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrapOr("Yellow!")); // "Yellow!" + * ``` + */ + unwrapOr(defaultVal: T): T { + return this.match( + (val) => val, + (_) => defaultVal + ); + } + + /** Tries to unwrap the error if vale is `Ok` returns `defaultVal` + * + * @param defaultVal + * @returns + * + * ## Usage + * + * ```ts + * result.unwrapErrOr("Error"); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrapErrOr("Yellow!")); // "Yellow!" + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrapErrOr("Yellow!")); // "oops!" + * ``` + */ + unwrapErrOr(defaultVal: E): E { + return this.match( + () => defaultVal, + (err) => err + ); + } + + /** Tries to return the value if value is `Err` calls `fn` + * + * @param fn Function called if `Err` + * + * ## Usage + * + * ```ts + * result.unwrapOrElse(() => "Hello!"); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrapOrElse(() => "oops!")); // "Hello!" + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrapOrElse(() => "Hello!")); // "Hello!" + * ``` + * + */ + unwrapOrElse(fn: (err: E) => T): T { + return this.match( + (val) => val, + (err) => fn(err) + ); + } + + /** Tries to return the error if value is `Ok` calls `fn` + * + * @param fn Function called if `Ok` + * + * ## Usage + * + * ```ts + * result.unwrapErrOrElse(() => "Error!"); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrapErrOrElse(() => "oops!")); // "oops!" + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.unwrapErrOrElse(() => "Hello!")); // "oops!" + * ``` + * + */ + unwrapErrOrElse(fn: (val: T) => E): E { + return this.match( + (val) => fn(val), + (err) => err + ); + } + + /** Tries to return value if value is `Err` throws custom error message. + * + * @param message Message to show when value is `Err` + * @returns + * + * ## Usage + * + * ```ts + * result.expect("Custom message"); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.expect("I failed!")); // "Hello!" + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * result.expect("I failed!"); // Error: I failed! + * ``` + */ + expect(message: string): T { + return this.match( + (val) => val, + () => { + throw new Error(message); + } + ); + } + + /** Tries to return error value if value is `Ok` throws custom error message + * + * @param message + * @returns + * + * ## Usage + * + * ```ts + * result.expectErr("Custom message"); + * ``` + * + * ## Examples + * + * ### When `Ok` + * + * ```ts + * const functionThatMightFail = (): Result => Ok("Hello!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.expectErr("I failed!")); // Error: I failed! + * ``` + * + * ### When `Err` + * + * ```ts + * const functionThatMightFail = (): Result => Err("oops!"); + * + * const result = functionThatMightFail(); + * + * console.log(result.expectErr("I failed!")); // "oops!" + * ``` + */ + expectErr(message: string): E { + return this.match( + () => { + throw new Error(message); + }, + (err) => err + ); + } +} + +/** Returns a new `Ok` result type with the provided value + * + * @param val Value of the result + * @returns + * + * ## Usage + * + * ```ts + * Ok(true); + * ``` + * + * ## Examples + * + * ```ts + * const functionThatCanFail = (condition) => { + * if (condition) { + * Ok("Success") + * } + * + * return Err("Failure"); + * } + * ``` + */ +const Ok = (val: T): Result => { + return new Result({ ok: true, val }); +}; + +/** Returns a new `Err` result type with the provided error + * + * @param err Error of the result + * @returns + * + * ## Usage + * + * ```ts + * Err("I failed!"); + * ``` + * + * ## Examples + * + * ```ts + * const functionThatCanFail = (condition) => { + * if (condition) { + * Ok("Success") + * } + * + * return Err("Failure"); + * } + * ``` + */ +const Err = (err: E): Result => { + return new Result({ ok: false, err }); +}; + +export { type Result, Ok, Err }; diff --git a/src/routes/components/ipv4address-input/+page.svelte b/src/routes/components/ipv4address-input/+page.svelte index e7dd835..778cdcd 100644 --- a/src/routes/components/ipv4address-input/+page.svelte +++ b/src/routes/components/ipv4address-input/+page.svelte @@ -14,3 +14,11 @@ +Reactive + + + +Validation + + + diff --git a/src/routes/components/ipv4address-input/basic.svelte b/src/routes/components/ipv4address-input/basic.svelte index f27910a..f56cb44 100644 --- a/src/routes/components/ipv4address-input/basic.svelte +++ b/src/routes/components/ipv4address-input/basic.svelte @@ -5,5 +5,5 @@
- +
diff --git a/src/routes/components/ipv4address-input/examples.ts b/src/routes/components/ipv4address-input/examples.ts index 0f6921e..4682a51 100644 --- a/src/routes/components/ipv4address-input/examples.ts +++ b/src/routes/components/ipv4address-input/examples.ts @@ -2,6 +2,10 @@ import Basic from './basic.svelte'; import basicRaw from './basic.svelte?raw'; import Placeholder from './placeholder.svelte'; import placeholderRaw from './placeholder.svelte?raw'; +import Reactive from './reactive.svelte'; +import reactiveRaw from './reactive.svelte?raw'; +import Valid from './valid.svelte'; +import validRaw from './valid.svelte?raw'; const examples = { basic: { @@ -11,6 +15,14 @@ const examples = { placeholder: { code: placeholderRaw, Component: Placeholder + }, + reactive: { + code: reactiveRaw, + Component: Reactive + }, + valid: { + code: validRaw, + Component: Valid } }; diff --git a/src/routes/components/ipv4address-input/reactive.svelte b/src/routes/components/ipv4address-input/reactive.svelte new file mode 100644 index 0000000..9b20e39 --- /dev/null +++ b/src/routes/components/ipv4address-input/reactive.svelte @@ -0,0 +1,13 @@ + + +
+ + + +
diff --git a/src/routes/components/ipv4address-input/valid.svelte b/src/routes/components/ipv4address-input/valid.svelte new file mode 100644 index 0000000..a816731 --- /dev/null +++ b/src/routes/components/ipv4address-input/valid.svelte @@ -0,0 +1,18 @@ + + +
+ + + + + Valid: + + {valid} + + +