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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@verisure-italy/funnel-types

v1.7.3

Published

Types for Funnel service

Readme

@verisure-italy/funnel-types

Technical README describing the type system and Zod schemas used to model Funnel, Funnel Path and Survey domain objects.

Purpose

This package centralizes strongly-typed, runtime-validated schemas for the Funnel service. It leverages Zod to provide:

  • Runtime validation of incoming / persisted payloads
  • Static TypeScript inference for compile‑time safety
  • Discriminated unions for polymorphic structures (questions, routing conditions)
  • Consistent error message keys for i18n / UI mapping

Consumers import only what they need; all symbols are re‑exported from src/index.ts for convenience.

Installation

In a workspace setup this is referenced via workspace:*. External usage:

pnpm add @verisure-italy/funnel-types

Build Overview

The package is built with tsup to produce both ESM and CJS bundles (dist/index.js / dist/index.cjs) plus type declarations. Tree‑shaking is supported because Zod schemas are plain JS objects.

Scripts:

  • pnpm build – build once with d.ts
  • pnpm dev – watch mode

Core Enumerations & Basic Types

export const funnelTypes = ['home', 'business'] as const
export type FunnelType = 'home' | 'business'

export const questionTypes = ['choices', 'zip_code', 'phone', 'phone_number', 'text'] as const
export type QuestionType = 'choices' | 'zip_code' | 'phone' | 'phone_number' | 'text'

export const routingConditions = [
  'exact', 'contains', 'starts_with', 'ends_with', 'out_of_area', 'match_all'
] as const
export type RoutingCondition =
  | 'exact' | 'contains' | 'starts_with' | 'ends_with'
  | 'out_of_area' | 'match_all'

All enumerations have companion Zod schemas (funnelTypeSchema, questionTypeSchema, routingConditionSchema) that give both validation and TypeScript inference.

Modeling Strategy

The package prefers explicit discriminated unions using a type discriminator key to:

  • Enforce different required fields per variant
  • Narrow TS types automatically after refinement
  • Avoid optional fields that are only relevant for subsets of variants

Routing Conditions

funnelRoutingSchema models conditional transitions between questions:

condition: z.discriminatedUnion('type', [
  // value must be a non-empty string for these types
  z.object({
    type: z.enum(['exact', 'contains', 'starts_with', 'ends_with']),
    value: z.string().min(1, 'value.required'),
  }),
  // value explicitly null when matching all
  z.object({ type: z.literal('match_all'), value: z.null() }),
  // boolean flag (default true) for out_of_area condition
  z.object({ type: z.literal('out_of_area'), value: z.boolean().default(true) }),
])

Semantics:

  • exact | contains | starts_with | ends_with: value is a required non-empty string used for pattern matching.
  • match_all: value is null, meaning unconditional match.
  • out_of_area: value is a boolean (defaults to true) indicating routing based on geolocation failure / area exclusion.

Question Variants

funnelQuestionSchema discriminates on type and constrains options accordingly:

  1. choices
    • options.responses: Array of { label, value } with at least one entry and enforced unique value set.
  2. phone | phone_number
    • options.onlyMobile: Boolean flag (e.g., restrict to mobile numbers).
  3. zip_code | text
    • options optional and currently empty (placeholder for future extensions).

This mirrors a previous Yup schema but yields native TS inference without manual type duplication.

Timestamp Extension

Schemas representing persisted entities (funnelSchema, funnelPathSchema, surveySchema) extend timestampFieldsSchema from @verisure-italy/shared-types, appending createdAt, updatedAt, etc. This keeps temporal metadata consistent across services.

Exported Schemas & Types (Summary)

| Symbol | Kind | Purpose | |--------|------|---------| | funnelContextSchema | Zod Object | Minimal context definition (code/title) | | funnelFlowQuestionSchema | Zod Object | Links context + question code in a flow | | funnelFlowSchema | Zod Object | Ordered list of questions + default flag | | funnelRoutingTargetSchema | Zod Object | A routing target (flow/question codes) | | funnelRoutingSchema | Zod Object | Conditional transition definition | | funnelQuestionSchema | Zod Discriminated Union | Polymorphic question model | | funnelSchema | Zod Object | Entire funnel aggregate | | funnelPathSchema | Zod Object | Path-specific configuration | | surveyMetaSchema | Zod Object | Collected metadata (client, UA, etc.) | | surveySchema | Zod Object | Submitted survey instance |

Each schema has a matching type (e.g., FunnelRouting) via z.infer<typeof schema>.

Usage Examples

Validating Input

import { funnelQuestionSchema } from '@verisure-italy/funnel-types'

const payload = {
  code: 'entry-product',
  script: 'choose_product',
  description: null,
  progress: 10,
  type: 'choices',
  options: {
    responses: [
      { label: 'Alarm', value: 'alarm' },
      { label: 'Camera', value: 'camera' },
    ],
  },
}

const parsed = funnelQuestionSchema.parse(payload) // Throws on validation error
// parsed inferred as FunnelQuestion (narrowed to choices variant)

Narrowing by Discriminator

function handleQuestion(q: FunnelQuestion) {
  switch (q.type) {
    case 'choices':
      // q.options.responses available and typed
      break
    case 'phone':
    case 'phone_number':
      // q.options.onlyMobile boolean
      break
    case 'zip_code':
    case 'text':
      // q.options possibly undefined
      break
  }
}

Safe Parsing

import { funnelRoutingSchema } from '@verisure-italy/funnel-types'

const result = funnelRoutingSchema.safeParse(maybePayload)
if (!result.success) {
  // result.error.format() can feed UI error mapping
}

Error Message Strategy

Validation constraints include custom message keys (e.g., code.required, slug.invalid_format) rather than raw human text. This supports localization and consistent front-end mapping. Consumers should maintain a dictionary mapping these keys to user-facing strings.

Adding a New Question Type

  1. Add new literal to questionTypes array in types.ts.
  2. Extend discriminated union in funnel-models.ts:
    • Define a new const newQuestionSchema = baseQuestionSchema.extend({ type: z.literal('your_new_type'), options: ... }).
    • Append schema to the funnelQuestionSchema discriminatedUnion array.
  3. Adjust any services relying on exhaustive switch(q.type) statements.
  4. Increment package version following semver (new type usually minor).

Adding a New Routing Condition

  1. Add literal to routingConditions in types.ts.
  2. Add corresponding object schema in the discriminated union in funnelRoutingSchema with proper value validation.
  3. Update control flow or matching logic in downstream services.

Extensibility Guidelines

  • Prefer extending via .extend() on a base schema to keep shared fields centralized.
  • Use z.discriminatedUnion when future variants are anticipated; it scales better than unions requiring manual refinement checks.
  • Keep default values in schemas (e.g., default(false)) rather than injecting outside; ensures consistent shape after .parse().

Performance Considerations

Zod validation is synchronous and relatively fast for these small structures. For high-frequency validation (e.g., bulk survey imports) consider:

  • Using .safeParse() to avoid exception overhead.
  • Caching compiled schemas (already inherently cached by module system).

Interoperability

Because schemas are runtime objects, they can be used to:

  • Generate OpenAPI (with additional tooling / transforms).
  • Serialize validation metadata for front-end form builders.
  • Provide consistent type guards across microservices.

Type Narrowing & Exhaustiveness

When switching on discriminators, enable TypeScript's --strict mode so non-exhaustive branches produce warnings. This protects against missing logic when adding new variants.

Import Paths

import {
  funnelSchema,
  funnelQuestionSchema,
  funnelRoutingSchema,
  Funnel,
  FunnelQuestion,
  FunnelRouting,
} from '@verisure-italy/funnel-types'

All exports are flattened. Avoid deep imports (e.g., @verisure-italy/funnel-types/dist/...) to retain compatibility with future internal restructuring.

Testing Recommendations

Use unit tests to assert both success and failure cases:

expect(() => funnelQuestionSchema.parse(invalid)).toThrow()
expect(funnelQuestionSchema.parse(valid).type).toBe('choices')

Focus on edge cases: empty responses, duplicate value, progress out of range, invalid routing condition/value combinations.

Versioning & Change Management

  • Adding new optional fields: MINOR version bump
  • Adding new required fields or changing validation: MAJOR version bump
  • Documentation updates / internal refactors: PATCH

FAQ

Q: Why Zod over Yup?
A: Native TS inference, discriminated union support, simpler extension, no separate type duplication.

Q: Can I serialize schemas?
A: Zod does not serialize schemas directly; for doc generation integrate with zod-to-openapi or custom mappers.

Q: How do I handle unknown extra properties?
A: Use .strict() on object schemas if you want to forbid unknown keys (current schemas are permissive). Adjust as needed.

Roadmap Ideas

  • Introduce number or date question types
  • Add internationalization metadata (labels per locale) to questions
  • Provide OpenAPI generation helper
  • Add helper functions for routing evaluation

License

Internal proprietary – published to npm with restricted usage under Verisure Italy organizational policies.


For questions or changes, open a PR referencing this README section you modify to keep documentation in sync.