npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

kitva

v1.0.0-next.15

Published

Validation kit for SvelteKit

Downloads

17

Readme

Kitva - Validation Kit for Sveltekit

Validate your endpoints and forms with no boilerplate, Just define your schemas.ts alongside your routes and this tool will take care of validation, type generation and form client generation.

Showcase

https://github.com/qurafi/kitva/assets/15172611/839dea17-95f2-476d-8b5f-f90dd12ce77d

Table of Content

Features

  • Standard: Uses Json Schema standard as the default schema format.
  • Performance: Compiles your schemas into highly optmized validation code, thanks to Ajv.
  • Less boilerplate: Endpoints and forms are automatically validated by a global Sveltekit hook.
  • Small bundle sizes: Just your validation function and the form client and nothing else!
  • Typesafety: Types are automatically generated and handled for you.
  • Fully featured form client: Just import ./$form inside your page and your form client is ready to use. With features including:
    • Fully typed, No need to bring types with you.
    • Save your form in session storage.
    • Warning before navigating away.
    • Form input components ready to use with binding and errors all set
    • and more.
  • Zod compatibility: Zod schemas are converted to Json Schemas out of the box.

Get Started

NOTE: This is still in alpha. If you have any issue or feedback, please raise a new issue.

npm i kitva ajv ajv-formats@beta

pnpm i kitva ajv ajv-formats@beta

And then run this command to setup everything:

npm run kitva

pnpm kitva

After that, you may need to restart your typescript server.

NOTE: This command will edit your vite config, tsconfig and create $lib/validation/hook file, so it's recommended to commit your work.

For more information: See Manual setup, CLI Options

Schema format

Json schemas are used as the default schema format and it's managed by Ajv. Althought we kinda made it possible to use other formats, but we currently only focus on json schemas.

For more information about using Json Schemas. Consult one of the following links:

NOTE: This library does not handle any schema compilation or any schema specific logic. It uses ajv-build-tools plugin under the hood to manage the compilation. If you have any issue regarding compilation, please open issue there.

Defining Schemas

There's two kind of schema files:

  • A shared one defined in $lib/schemas.
  • A route schema which included with some enhancement for endpoints and forms.

Example of an endpoint schema file

/* 
    routes/api/login
        +server.ts
        schemas.ts
*/

// or GET, DELETE, etc.
export const POST = {
 body: {
  type: "object",
  properties: {
   username: {
    type: "string",
    minLength: 3,
    maxLength: 36
   },
   password: {
    type: "string",
    minLength: 6,
    maxLength: 128,
    format: "password" // just a hint for the UI
   },
   email: {
    type: "string",
    minLength: 6,
    maxLength: 100,
    format: "email"
   }
  },
  // additional properties allowed by default per the standard
  additionalProperties: false,
  // properties are optional by default
  required: ["username", "password", "email"]
 }
 // validate other parts
 // queries, headers, params
};

And that's it. Your endpoint will automatically validated. To get the parsed data use the event.locals.validation.*

// all related types available in the new $types2 file
import type { POSTHandler } from "./$types2";

export const POST: POSTHandler = async (event) => {
    // data is fully typed
    // {email:string, username: string, ...}
    const { data } = event.locals.validation.body;
    return text("ok");
};

The event local is automatically set by the hook and it contains:

event.locals.validation = {
    valid: boolean, // if any part failed
    body/headers/queries/params: {
        valid: boolean,
        data: Data,
        input: JSONType,
        errors: AjvError[]
    },
}

Type builders

You could use some type builders such as Zod1, Fluent-Json-Schema and TypeBox2 to make life easier:

import { Type as t } from "@sinclair/typebox";
import { z } from "zod";

// typebox
const UserLoginSchemaTypeBox = t.Object(
 {
  username: t.String({
   minLength: 3,
   maxLength: 36
  }),
  password: t.String({
   format: "password", // just a hint for the UI
   minLength: 6,
   maxLength: 128
  }),
  email: t.String({
   format: "email",
   minLength: 6,
   maxLength: 100
  })
 },
 { additionalProperties: false }
);

// zod
const UserLoginSchemaZod = z.object({
 username: z.string().min(3).max(36),
 password: z.string().min(6).max(128),
 email: z.string().min(6).max(100).email()
});

export const POST = {
 body: UserLoginSchemaTypeBox,
//  body: UserLoginSchemaZod,
};

1: Zod schemas are converted to json schema using zod-to-json-schema. Not all features are supported and the validation and compilation still backed by Ajv. Although it should work fine for most of schemas.

2: TypeBox supports type inference but currently all schemas are converted to types by json-schema-to-typescript.

Form actions:

/* routes/api/login
    +page.svelte
    +page.server.ts
    schema.ts
*/
export const actions = {
    signup: {
        // must be type object and additionalProperties turned off
        type:"object",
        additionalProperties: false,
        properties: {
            a: {type:"string"},
            b: {type:"boolean", default:false},
        },
        // must set required
        required: ["a", "b"]
        ...
    }

}

This will require aditional step by calling withValidation as there's no current possible way to intercept and change form result in Sveltekit:

import { withValidation } from "kitva/hooks";
// NOTE: use $types2
import type { Actions } from "./$types2";

export const actions: Actions = withValidation({
    signup(event) {
        // type safe!
        event.locals.validation.body.a

        return {
            success: true,
        };
    },
    another: ..., // TS error, unknown prop
});

Using the client:

To use the client, simply import ./$form. The types will be automatically handled.

All clients are exported by the format, action_name = createActionNameForm, e.g., default = createDefaultForm.

<script>
 import { createDefaultForm } from "./$form";

 const my_form = createDefaultForm({fields: initial_fields});

 const { fields, errs, action, action_url } = my_form;
</script>

<form method="post" action={action_url}>
 <label>
  Username
  <input type="text" name="username" autocomplete="username" bind:value={$fields.username} />
  {#if $errs.username}
   <p class="error">{$errs.username}</p>
  {/if}
 </label>

 <!-- or use Input from kitva/components -->
 <Input form={my_form} name="username"/>
 ...
</form>

For more information about the client see forms/types.ts

Validation Hooks

To change the behavior of validation, use handleValidate from kitva/hooks.

handleValidate(actions.default, async ({ event, input, validate }) => {

 if (input.body) {
  input.body.filled_by_server = "filled by server";
 }

 await validate();

 delete input.body.password;

//  return false to cancel the validation
//  return false
});

Note that if you don't call validate. In production, it will be called for you, but in dev mode, an error will be thrown. This to prevent security issues when forgetting to call validate.

Standalone Validation

To directly import and use the compiled validation functions. refer to ajv-build-tools

CLI options

Running the command without arguments will setup every thing listed in Manual Setup, but in case you want to setup a specific setup add --only=steps, where steps are comma seperated: pnpm kitva --only=hook,vite,types

Manual Setup

Usually the CLI will handle most of the setup steps automatically, but just in case, here is the step required to setup Kitva:

  1. Vite plugin

    import { defineConfig } from "vite";
    import { sveltekit } from "@sveltejs/kit/vite";
    import { vitePluginSvelteKitva } from "kitva/vite";
    
    export default defineConfig({
     plugins: [sveltekit(), vitePluginSvelteKitva()]
    });
  2. Sveltekit hook

    // virtual import used to import all the compiled schemas
    import schemas from "$schemas?t=all";
    
    import { validationHook } from "kitva/hooks";
    import { createPreset } from "kitva/presets/ajv/server";
    
    export const preset = createPreset(schemas);
    
    export const handle = validationHook(preset);
  3. Setting up rootDir Similar to ".svelte-kit/types" for route types(./$types). Add ".schemas/types" to your rootDirs and include.
    NOTE: Because tsconfig does not merge rootDirs and include, you have to copy them from .svelte-kit/tsconfig.json.

     {
         "extends": "./.svelte-kit/tsconfig.json",
         "compilerOptions": {
             "rootDirs": [
                 ".",
                 ".svelte-kit/types",
                 ".schemas/types"
             ],
             "allowJs": true,
             "checkJs": true,
             "esModuleInterop": true,
             "forceConsistentCasingInFileNames": true,
             "resolveJsonModule": true,
             "skipLibCheck": true,
             "sourceMap": true,
             "strict": true
         },
         "include": [
             ".schemas/types"
             ".svelte-kit/ambient.d.ts",
             ".svelte-kit/types/**/$types.d.ts",
             "vite.config.ts",
             "src/**/*.js",
             "src/**/*.ts",
             "src/**/*.svelte",
             "src/**/*.js",
             "src/**/*.ts",
             "src/**/*.svelte",
             "tests/**/*.js",
             "tests/**/*.ts",
             "tests/**/*.svelte",
         ],
     }
     ```
    
  4. Ambient Types. Just add this import "kitva/ambient"; on top of your app.d.ts. This will type $schemas virtual imports