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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sourceregistry/sveltekit-enhance

v1.4.0

Published

Composable enhance and form utilities for SvelteKit actions, loads, methods, and hooks.

Downloads

216

Readme

@sourceregistry/sveltekit-enhance

Composable middleware, guards, and form utilities for SvelteKit

npm version npm downloads license SvelteKit issues

Wrap actions, loads, methods, and hooks with composable enhancers. Stack auth guards, feature flags, request tracing, and form parsing without touching SvelteKit's internals.

Docs · npm · Issues


Installation

npm install @sourceregistry/sveltekit-enhance

Peer dependency: @sveltejs/kit ^2.58.0


Overview

import { enhance, Auth, RequestCorrelation, RequestMonitor, Form } from '@sourceregistry/sveltekit-enhance';

// hooks.server.ts
export const handle = enhance.handle(
    async ({ event, resolve }) => resolve(event),
    RequestCorrelation.attach,
    RequestMonitor.trace({ logger: myLogger, record: metrics.record }),
);

// +server.ts
export const POST = enhance.method(
    async (event) => new Response(JSON.stringify(event.context)),
    Auth.Bearer,
    FeatureFlag.all('PUBLIC_API_ENABLED'),
);

// +page.server.ts
export const actions = {
    default: enhance.action(
        async (event) => {
            const name = event.context.form.string$('name');
            return success({ name });
        },
        Form.schema(myValidator),
    ),
};

Core API

Import from @sourceregistry/sveltekit-enhance.

enhance.handle

Wraps SvelteKit's handle hook. Enhancers run left-to-right before the handler; their return values are merged into context.

import { enhance } from '@sourceregistry/sveltekit-enhance';

// src/hooks.server.ts
export const handle = enhance.handle(
    async ({ event, resolve, context }) => resolve(event),
    enhancerA,
    enhancerB,
);

SSE / streaming responses

enhance.handle automatically detects Server-Sent Events and other streaming requests (via Accept: text/event-stream) and bypasses the main handler entirely — going straight to SvelteKit's resolve. This prevents the handler from blocking indefinitely on await resolve(event), which never settles for a streaming response.

Consequences:

  • The main handler does not run for SSE requests — it cannot inspect or modify the response headers.
  • responseHandlers (added by enhancers via contextInput.responseHandlers.push(...)) are also skipped for responses with Content-Type: text/event-stream.
  • Enhancers (the variadic context arguments) still run normally — they can set locals, authenticate the request, or short-circuit with an error response.
// This handler is skipped for SSE requests — no change needed on your end.
export const handle = enhance.handle(
    async ({ event, resolve }) => {
        const response = await resolve(event); // not called for SSE
        response.headers.set('x-custom', 'value');
        return response;
    },
    Auth.Bearer,       // still runs — can reject unauthorized SSE requests
    SessionGuard.hook, // still runs
);

If you need to set response headers on SSE routes, do it in the route handler itself (e.g. +server.ts) rather than in the enhance.handle main handler.

enhance.load

Wraps server load functions.

// +page.server.ts
export const load = enhance.load(
    async (event) => ({ user: event.context.user }),
    Auth.Bearer,
);

enhance.action

Wraps form actions.

// +page.server.ts
export const actions = {
    submit: enhance.action(
        async (event) => success(event.context),
        Auth.Bearer,
        Form.schema(myValidator),
    ),
};

enhance.method

Wraps +server.ts endpoint handlers.

// +server.ts
export const GET = enhance.method(
    async (event) => new Response(JSON.stringify(event.context)),
    Auth.Bearer,
);

Utilities

import { fail, error, success, not_good } from '@sourceregistry/sveltekit-enhance';

fail(400, { message: 'bad input' });   // throws ActionFailure — use inside actions
error(404, { message: 'not found' });  // throws HttpError
success({ id: 1 });                    // typed identity helper
not_good(input, 403);                  // delegates to fail or error based on callType

Helpers

All helpers are available from @sourceregistry/sveltekit-enhance or @sourceregistry/sveltekit-enhance/helpers.


CSRF

Blocks cross-site form submissions on mutating methods (POST, PUT, PATCH, DELETE) with form content types. Checks the Origin header against the request origin. Absent Origin (server-side fetch, curl) is allowed through. Returns 403 — JSON body when Accept: application/json, SvelteKit error() otherwise.

import { CSRF, CSRFChecker } from '@sourceregistry/sveltekit-enhance';

export const handle = enhance.handle(
    myHandler,
    CSRF.inspect(
        CSRFChecker.list('/api/webhooks/stripe'),  // bypass paths
        myLogger,                                   // optional, defaults to console
    ),
);

Built-in bypass checkers:

| Checker | Description | |---------|-------------| | CSRFChecker.list(...paths) | Exact pathname match | | CSRFChecker.regex(...patterns) | RegExp match against pathname |

Custom checker — any (input: EnhanceInput) => MaybePromise<boolean>:

// true = bypass CSRF check
CSRF.inspect((input) => input.url.pathname.startsWith('/api/public'))

Returns { csrf_valid: true } on pass. Locals set: none.


Auth

Extracts and validates Authorization: Bearer <token> headers.

import { Auth } from '@sourceregistry/sveltekit-enhance';

export const GET = enhance.method(
    async (event) => new Response(event.context.token),
    Auth.Bearer,
);

Returns { token: string }. Throws 401 if the header is missing or malformed.


Devtools

Silences Chrome DevTools probe requests (/.well-known/appspecific/com.chrome.devtools.json) with a 204 No Content. Logs in dev mode.

import { Devtools } from '@sourceregistry/sveltekit-enhance';

export const handle = enhance.handle(myHandler, Devtools.ignore);

FeatureFlag

Guards routes behind SvelteKit public env vars ($env/dynamic/public). Always passes in dev mode.

import { FeatureFlag } from '@sourceregistry/sveltekit-enhance';

// All listed flags must be enabled
FeatureFlag.all('PUBLIC_FEATURE_A', 'PUBLIC_FEATURE_B')

// At least one flag must be enabled
FeatureFlag.oneOf('PUBLIC_FEATURE_A', 'PUBLIC_FEATURE_B')

Truthy values: true, TRUE, on, ON, 1. Returns { flags } or throws 503 Feature not enabled.


RequestCorrelation

Propagates a correlation ID across the request/response cycle.

  • Reads x-correlation-id or x-request-id from incoming headers
  • Validates: max 128 chars, pattern [A-Za-z0-9._:-]+
  • Generates a UUID v4 if absent
  • Echoes the ID back via x-correlation-id response header
import { RequestCorrelation } from '@sourceregistry/sveltekit-enhance';

export const handle = enhance.handle(myHandler, RequestCorrelation.attach);

| Local | Type | Description | |-------|------|-------------| | correlation_id | string | Resolved correlation ID | | request_started_at | number | Date.now() at attach time |


RequestMonitor

Structured HTTP request logging and optional metrics collection. Instruments the full lifecycle: start, completion (log level by status), and unhandled errors — all with elapsed duration.

import { RequestMonitor } from '@sourceregistry/sveltekit-enhance';

export const handle = enhance.handle(
    myHandler,
    RequestCorrelation.attach,
    RequestMonitor.trace({
        logger: myLogger,        // optional, defaults to console
        record: metrics.record,  // optional
    }),
);

TraceOptions

| Option | Type | Default | Description | |--------|------|---------|-------------| | logger | TraceLogger | console | Must implement debug, info, warn, error | | record | (entry: RecordTraceMetricEntry) => any | — | Called after every request |

Log events

| Event | Level | Condition | |-------|-------|-----------| | http.request.started | debug | Before resolve | | http.request.completed | info | status < 400 | | http.request.completed | warn | status 4xx | | http.request.completed | error | status 5xx | | http.request.failed | error | Unhandled throw |

Types

type RecordTraceMetricEntry = { method: string; path: string; status: number; durationMs: number }
type TraceLogger            = { debug(...args: any[]): any; info(...args: any[]): any; warn(...args: any[]): any; error(...args: any[]): any }

Locals set: trace: { id: string; started_at: bigint }.


CacheControl

Sets Cache-Control response headers. Provides named directive helpers so policies are readable at a glance, and two enhancers — one for broad handle-level rules, one for per-route load-level overrides.

import { CacheControl, directive } from '@sourceregistry/sveltekit-enhance';

// hooks.server.ts — apply rules globally
export const handle = enhance.handle(
    myHandler,
    CacheControl.global(
        { match: /^\/api\//, directive: directive.noStore },
        { match: /^\/blog\//, directive: directive.public(300, 60) },
    ),
);

// +page.server.ts — override for a specific route
export const load = enhance.load(
    async (event) => event.context,
    CacheControl.local(directive.public(3600)),
);

directive is also exported as a standalone named export — shorthand for CacheControl.directive when you only need to reference directives without the enhancers.

CacheControl.global(...rules)

Handle-level enhancer. Iterates the rule list and sets Cache-Control via setHeaders() for the first matching pathname. When no rule matches, the header is left unset.

| Param | Type | Description | |-------|------|-------------| | ...rules | CacheRule[] | Ordered list of { match, directive } pairs. First match wins. |

Runs in handle context only. Add a catch-all rule (match: () => true) if you want a guaranteed fallback.

CacheControl.local(directive)

Load-level enhancer. Sets Cache-Control for the current route via setHeaders(). Use this to override a global rule for a specific page.

| Param | Type | Description | |-------|------|-------------| | directive | CacheDirective | The directive string to apply. |

CacheControl.directive

Named helpers that produce correctly-formatted Cache-Control strings.

| Helper | Directive | When to use | |--------|-----------|-------------| | noStore | no-store | Sensitive data — never cache (user dashboards, auth responses) | | noCache | no-cache | Changes frequently but supports conditional GETs (ETags / Last-Modified) | | noCacheNoStore | no-cache, no-store, must-revalidate | Belt-and-suspenders; also covers legacy HTTP/1.0 proxies | | public(maxAge, swr?) | public, max-age=N[, stale-while-revalidate=N] | Publicly cacheable content; swr serves stale while revalidating in the background | | private(maxAge) | private, max-age=N | Personalised content — browser cache only, not CDNs or shared proxies |


Form

Typed, ergonomic FormData extraction. Works standalone or as an enhancer.

As an enhancer — Form.schema

Validates and deserializes form data via a Validator<T> before the handler runs.

import { Form, enhance, success } from '@sourceregistry/sveltekit-enhance';

export const actions = {
    default: enhance.action(
        async (event) => success(event.context.form.result),
        Form.schema(myValidator),
    ),
};

Standalone — Form.handle

import { Form } from '@sourceregistry/sveltekit-enhance';

await Form.handle(request, ({ form }) => {
    const name = form.string$('name');
    const age  = form.number('age');
    return { name, age };
});

Field extractors

Optional variants return undefined when the field is absent. Required variants ($ suffix) throw fail(400).

| Method | Returns | Notes | |--------|---------|-------| | string(name) / string$(name) | string \| null \| undefined | | | pattern$(name, pattern) | string | RegExp or pattern string | | number(name) / number$(name) | number \| undefined | | | boolean(name) / boolean$(name) | boolean \| undefined | Accepts true/false, 1/0, on/off | | date(name, parser?) / date$(name, parser) | Date \| undefined | Custom parser supported | | json<T>(name, transformer?) / json$(name) | T \| undefined | Optional transform fn | | jsond(options) | any | All FormData → nested object via dot-notation keys | | file(name) / file$(name) | File \| null \| undefined | | | files(name) | File[] | Non-empty, named files only | | fileRecord(prefix, removePrefix?) | Record<string, File[]> | Groups files by key prefix | | array<T>(name, mapper?) / array$(name) | T[] \| undefined | | | enum(name, Enum) / enum$(name, Enum) | keyof E \| undefined | | | record(options?) | Record<string, any> | All entries; optional filter / transformer | | validate(schema, options?) | T | Runs Validator<T>, throws fail(400) on failure |

Conditional helpers

form.onlyIf(condition, trueVal, falseVal?)
form.onlyIfPresent(key, (entry) => ..., fallback?)
form.onlyIfArrayPresent(key, (entries) => ..., fallback)
form.selector({ fieldName: (entry, key) => ..., $default?, $error? })
form.selector$({ ... })    // throws fail(400) if no case matches
form.basedOn(val, processor)
form.process(name, parser, processor)

Standalone functions

All FormContext methods are also exported as standalone functions taking FormData as the first argument, plus:

arrayString(formdata, name, delimiter, mapper?)
hasOneOf(formdata, names)
reviver(key, value)   // JSON.parse reviver — coerces strings to typed primitives

Type Reference

// Core
EnhanceInput<CallType>     // event passed to all enhancers — includes all RequestEvent properties:
                           //   cookies, fetch, locals, params, request, route, url
                           //   setHeaders(headers)      — set response headers (no set-cookie; use cookies API)
                           //   isDataRequest            — true when client fetches +page/layout.server.js data
                           //   isSubRequest             — true for same-origin server-side fetch (no HTTP round-trip)
                           //   isRemoteRequest          — true when request comes from a remote function
                           //   tracing                  — OpenTelemetry spans (no-ops when tracing disabled; since SK 2.31)
                           //   + handle: resolve, event, responseHandlers
                           //   + load:   parent, depends, untrack
EnhanceFunction<CallType>  // (event: EnhanceInput) => MaybePromise<object | Response>
EnhanceHandle              // final handle fn — ({ event, resolve, context }) => MaybePromise<Response>
EnhanceLoad                // final load fn
EnhanceAction              // final action fn
EnhanceMethod              // final method fn
MaybePromise<T>            // T | Promise<T>

// Form
Validator<T>               // (value: unknown, path?: ValidationPath) => ValidationResult<T>
ValidationResult<T>        // { success: true; data: T } | { success: false; errors: ValidationIssue[] }
ValidationIssue            // { path: string; message: string; code?: string }
FormContext                // fluent API returned by Form.enhance / Form.handle
InferValidator<V>          // infers T from Validator<T>

// Helpers
TraceLogger                // { debug, info, warn, error }
TraceOptions               // { logger?: TraceLogger; record?: (entry) => any }
RecordTraceMetricEntry     // { method: string; path: string; status: number; durationMs: number }
RequestTraceLocals         // { trace?: { id: string; started_at: bigint } }
RequestCorrelationLocals   // { correlation_id?: string; request_started_at?: number }
CSRFChecker                // { regex(...patterns): checker; list(...paths): checker }
CacheDirective             // union of valid Cache-Control strings (+ open string & {})
CacheRule                  // { match: RegExp | ((pathname) => boolean); directive: CacheDirective }

License

Apache-2.0 © A.P.A. Slaa