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

@ffsm/serialize

v0.0.2

Published

Utilities for serialize

Downloads

9

Readme

Serialization string

Installation

npm i @ffsm/serialize

OR

yarn add @ffsm/serialize

Usage

Import and use as a named function:

import { encode } from '@ffsm/serialize`;

encode('&');

OR import and use from the default export:

import Serialize from '@ffsm/serialize';

Serialize.encode('&');

decode

Safely decodes URL-encoded strings with error handling.

import { decode } from '@ffsm/serialize';

// Basic usage - decodes with decodeURIComponent (default)
const text = decode('Hello%20World'); // 'Hello World'

// Use decodeURI instead (preserves certain URL characters)
const url = decode('https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue', false);
// 'https://example.com/path?query=value'

// Custom decoder function
const emoji = decode('%F0%9F%8C%9F', myCustomDecoder);

// Graceful error handling
const problematic = decode('This%20has%2G%an%20error');
// Returns with best-effort decoding, logs error but doesn't throw exception

The function handles null and undefined values gracefully, returning an empty string. If decoding fails for certain segments, it maintains the original encoded value and logs an error to the console rather than throwing exceptions.

encode

Safely encodes strings for URL usage with error handling.

import { encode } from '@ffsm/serialize';

// Basic usage - encodes with encodeURIComponent (default)
const encoded = encode('Hello World'); // 'Hello%20World'

// Use encodeURI instead (preserves URL structure characters)
const urlSafe = encode('https://example.com/path?query=value', false);
// 'https://example.com/path?query=value'

// Custom encoder function
const custom = encode('Special chars: @#$%', myCustomEncoder);

// Graceful error handling for problematic strings
const safe = encode('Text with invalid \uD800 surrogate character');
// Handles the encoding without throwing exceptions

The function returns an empty string for null or undefined inputs. If encoding encounters errors with certain characters, it maintains those characters as-is and logs an error to the console rather than throwing exceptions, ensuring your application continues to run smoothly.

get

Safely access nested properties in objects and arrays without causing errors.

import { get } from '@ffsm/serialize';

const user = {
  name: 'John',
  profile: {
    contact: {
      email: '[email protected]',
      phone: null,
    },
    preferences: ['dark-mode', 'notifications'],
  },
};

// Simple property access
const name = get(user, 'name'); // 'John'

// Deep property access (as string path)
const email = get(user, 'profile.contact.email'); // '[email protected]'

// Deep property access (as array path)
const email2 = get(user, ['profile', 'contact', 'email']); // '[email protected]'

// Array element access
const pref = get(user, 'profile.preferences.0'); // 'dark-mode'

// Providing default values when property doesn't exist
const address = get(user, 'profile.contact.address', 'N/A'); // 'N/A'

// Type safety with generics
const phone = get<string>(user, 'profile.contact.phone', 'No phone'); // 'No phone'

This function helps you avoid the infamous "Cannot read property 'x' of undefined" errors by safely navigating through object hierarchies. It works with both dot notation strings and array paths for flexibility.

parse

Converts a URL query string into a structured object with support for various formats.

import { parse } from '@ffsm/serialize';

// Basic parsing
const params = parse('name=John&age=30');
// { name: 'John', age: '30' }

// Auto-convert types
const typedParams = parse('active=true&count=42', {
  parseBooleans: true,
  parseNumbers: true,
});
// { active: true, count: 42 }

// Parse arrays in different formats
const bracketArray = parse('colors[]=red&colors[]=blue', {
  arrayFormat: 'bracket',
});
// { colors: ['red', 'blue'] }

const indexArray = parse('colors[0]=red&colors[1]=blue', {
  arrayFormat: 'index',
});
// { colors: ['red', 'blue'] }

const commaArray = parse('colors=red,blue', { arrayFormat: 'comma' });
// { colors: ['red', 'blue'] }

// Parse nested objects
const nested = parse('user[name]=John&user[profile][age]=30');
// { user: { name: 'John', profile: { age: '30' } } }

// Parsing from URL with query prefix
const fromUrl = parse('?sort=desc&page=2', { ignoreQueryPrefix: true });
// { sort: 'desc', page: '2' }

Options

Configure parsing behavior with SerializeParseOptions:

| Option | Type | Default | Description | | ---------------------- | ------- | ------- | --------------------------------------------------------------------------- | | arrayFormat | string | 'none' | How arrays are formatted ('bracket', 'index', 'comma', 'separator', 'none') | | arrayFormatSeparator | string | ',' | Character separating array values when using 'comma' or 'separator' | | parseNumbers | boolean | false | Auto-convert numeric strings to numbers | | parseBooleans | boolean | false | Auto-convert 'true'/'false' strings to booleans | | decode | boolean | true | Whether to decode URI encoded components | | ignoreQueryPrefix | boolean | true | Whether to remove '?' prefix from query string |

query

Converts a JavaScript object into a URL query string with support for various formats.

import { query } from '@ffsm/serialize';

// Basic object to query string
const basic = query({ name: 'John', age: 30 });
// 'name=John&age=30'

// Format arrays in different ways
const bracketArray = query(
  { colors: ['red', 'blue'] },
  {
    arrayFormat: 'bracket',
  }
);
// 'colors[]=red&colors[]=blue'

const indexArray = query(
  { colors: ['red', 'blue'] },
  {
    arrayFormat: 'index',
  }
);
// 'colors[0]=red&colors[1]=blue'

const commaArray = query(
  { colors: ['red', 'blue'] },
  {
    arrayFormat: 'comma',
  }
);
// 'colors=red,blue'

// Custom array separator
const customSep = query(
  { tags: ['javascript', 'typescript'] },
  {
    arrayFormat: 'separator',
    arrayFormatSeparator: '|',
  }
);
// 'tags=javascript|typescript'

// Handle nested objects
const nested = query({
  user: {
    name: 'John',
    profile: { age: 30 },
  },
});
// 'user[name]=John&user[profile][age]=30'

// Skip null or undefined values
const skipNulls = query({ name: 'John', email: null }, { skipNull: true });
// 'name=John'

// Skip empty strings
const skipEmpty = query({ name: 'John', bio: '' }, { skipEmptyString: true });
// 'name=John'

// Sort parameters
const sorted = query({ z: 3, a: 1, b: 2 }, { sort: true });
// 'a=1&b=2&z=3'

// Custom sorting function
const customSort = query(
  { z: 3, a: 1, b: 2 },
  {
    sort: (a, b) => b.localeCompare(a), // reverse sort
  }
);
// 'z=3&b=2&a=1'

Options

Configure serialization behavior with SerializeQueryOptions:

| Option | Type | Default | Description | | ---------------------- | ---------------- | ------- | --------------------------------------------------------------------------- | | arrayFormat | string | 'none' | How arrays are formatted ('bracket', 'index', 'comma', 'separator', 'none') | | arrayFormatSeparator | string | ',' | Character separating array values when using 'comma' or 'separator' | | skipNull | boolean | false | Whether to omit null and undefined values | | skipEmptyString | boolean | false | Whether to omit empty string values | | encode | boolean | true | Whether to encode URI components | | strict | boolean | true | Whether to validate keys strictly | | sort | boolean/function | false | Whether and how to sort the parameters |

url

Replaces named parameters in a URL template with actual values.

import { url } from '@ffsm/serialize';

// Simple parameter replacement
const profileUrl = url('/users/:username', { username: 'john.doe' });
// '/users/john.doe'

// Multiple parameters in a route
const articleUrl = url('/blog/:category/:slug/:id', {
  category: 'technology',
  slug: 'javascript-tips',
  id: 42,
});
// '/blog/technology/javascript-tips/42'

// Same parameter used multiple times
const duplicateParams = url('/products/:id/reviews/:reviewId/by/:id', {
  id: 'abc123',
  reviewId: 789,
});
// '/products/abc123/reviews/789/by/abc123'

// With primitive types
const apiUrl = url('/api/:version/filter/:active/:count', {
  version: 'v2',
  active: true,
  count: 50,
});
// '/api/v2/filter/true/50'

// Missing parameters are replaced with empty string
const partialUrl = url('/users/:id/posts/:postId', { id: 123 });
// '/users/123/posts/'

// No parameters to replace
const staticUrl = url('/about-us', {});
// '/about-us'

The function provides a simple way to build URLs with dynamic parameters, similar to how routing works in modern web frameworks. It safely handles different primitive types (string, number, boolean) and replaces missing parameters with empty strings rather than throwing errors.

variable

Replaces variable placeholders in a string template with actual values.

import { variable } from '@ffsm/serialize';

// Basic template substitution
const greeting = variable('Hello, {name}!', {
  params: { name: 'John' },
});
// 'Hello, John!'

// Multiple variables
const userInfo = variable('Name: {name}, Email: {email}, Role: {role}', {
  params: {
    name: 'Alice',
    email: '[email protected]',
    role: 'Admin',
  },
});
// 'Name: Alice, Email: [email protected], Role: Admin'

// Accessing nested properties with dot notation
const profileText = variable('Profile: {user.name} ({user.details.age})', {
  params: {
    user: {
      name: 'Bob',
      details: {
        age: 32,
        location: 'New York',
      },
    },
  },
});
// 'Profile: Bob (32)'

// Custom formatting
const eventDetails = variable('Event: {title} on {date}', {
  params: {
    title: 'Company Meeting',
    date: '2023-09-15',
  },
  format: {
    title: (title) => title.toUpperCase(),
    date: (date) => {
      const [year, month, day] = date.split('-');
      return `${month}/${day}/${year}`;
    },
  },
});
// 'Event: COMPANY MEETING on 09/15/2023'

// Array values
const listItems = variable('Selected items: {items.0}, {items.1}, {items.2}', {
  params: {
    items: ['Apple', 'Banana', 'Cherry'],
  },
});
// 'Selected items: Apple, Banana, Cherry'

The function provides a flexible template system for string interpolation with:

  • Variable substitution with {variableName} syntax
  • Deep property access with dot notation: {user.profile.name}
  • Custom formatting through synchronous formatter functions
  • Multiple occurrences of the same variable
  • Safe handling of missing variables (replaced with empty string)

For asynchronous operations (API calls, database queries), use variableAsync.

variableAsync

Asynchronously replaces variable placeholders in a string template with actual values, supporting async formatters.

import { variableAsync } from '@ffsm/serialize';

// Basic template substitution (similar to variable)
const greeting = await variableAsync('Hello, {name}!', {
  params: { name: 'John' },
});
// 'Hello, John!'

// With async formatters
const userProfile = await variableAsync('User: {userId}', {
  params: { userId: 1234 },
  format: {
    userId: async (id) => {
      // Fetch user data from API
      const response = await fetch(`/api/users/${id}`);
      const user = await response.json();
      return `${user.name} (${user.status})`;
    },
  },
});
// 'User: John Doe (active)'

// Multiple async formatters processed in parallel
const weatherReport = await variableAsync(
  'Weather: {city1} | {city2} | {city3}',
  {
    params: {
      city1: 'New York',
      city2: 'London',
      city3: 'Tokyo',
    },
    format: {
      city1: async (city) => await fetchWeatherData(city),
      city2: async (city) => await fetchWeatherData(city),
      city3: async (city) => await fetchWeatherData(city),
    },
  }
);
// All three weather API calls happen concurrently

// Formatted date with timezone adjustment
const eventTime = await variableAsync('Event starts: {startTime}', {
  params: { startTime: '2023-09-15T14:00:00Z' },
  format: {
    startTime: async (time) => {
      // Could involve timezone API call or complex async calculation
      const localTime = await convertToLocalTimezone(time);
      return localTime.toLocaleString();
    },
  },
});
// 'Event starts: 9/15/2023, 10:00:00 AM'

When to use variableAsync vs variable

Use variableAsync when you need:

  • Async data sources: Fetching data from APIs, databases, or file systems
  • Complex transformations: Processing that requires promises or async/await
  • Parallel processing: All variables are processed concurrently for better performance
  • External services: Integration with services requiring network requests

Use variable (synchronous version) for simpler cases where:

  • You only need basic variable substitution
  • All data is already available in memory
  • Performance is critical and async overhead isn't needed

variableAsync provides all the same templating features as variable, plus:

  • Support for async formatter functions
  • Parallel processing of multiple variables using Promise.all
  • Seamless integration with async/await workflows

isString

Checks if a value is a string with TypeScript type guard support.

import { isString } from '@ffsm/serialize';

// Basic usage
const value = getUserInput();
if (isString(value)) {
  // TypeScript knows value is a string here
  const normalized = value.trim().toLowerCase();
  console.log(`Processing string: ${normalized}`);
} else {
  console.log('Expected a string value');
}

// Using with mapping functions
const processedValues = mixedData
  .filter(isString)
  .map((str) => str.toUpperCase());
// processedValues is string[]

This function not only checks if a value is a string at runtime but also serves as a TypeScript type guard, providing type narrowing in conditional blocks for improved type safety.

isNumber

Checks if a value is a number with TypeScript type guard support.

import { isNumber } from '@ffsm/serialize';

// Basic usage
const value = getValueFromAPI();
if (isNumber(value)) {
  // TypeScript knows value is a number here
  const formatted = value.toFixed(2);
  console.log(`Numeric value: ${formatted}`);
} else {
  console.log('Expected a numeric value');
}

// In data processing pipelines
const numericValues = dataset.filter(isNumber).filter((num) => num > 0);
// numericValues is number[]

// For calculations
function calculateAverage(values: unknown[]) {
  const numbers = values.filter(isNumber);
  if (numbers.length === 0) return 0;
  return numbers.reduce((sum, num) => sum + num, 0) / numbers.length;
}

This function checks if a value is a number at runtime while also serving as a TypeScript type guard, allowing you to safely access number methods in conditional blocks.

isBoolean

Checks if a value is a boolean with TypeScript type guard support.

import { isBoolean } from '@ffsm/serialize';

// Basic usage
const value = getConfigValue('featureEnabled');
if (isBoolean(value)) {
  // TypeScript knows value is a boolean here
  if (value) {
    enableFeature();
  } else {
    disableFeature();
  }
} else {
  console.log('Expected a boolean configuration value');
}

// With optional chaining
function getFeatureState(config: unknown) {
  if (isBoolean(config?.features?.darkMode)) {
    return config.features.darkMode ? 'enabled' : 'disabled';
  }
  return 'not configured';
}

// Filtering boolean flags
const enabledFeatures = Object.entries(featureFlags)
  .filter(([_, value]) => isBoolean(value) && value)
  .map(([key]) => key);

This function verifies a value is strictly a boolean (true or false) at runtime while also acting as a TypeScript type guard, making it safer to use in conditional logic.

isPrimitive

Checks if a value is a primitive (string, number, boolean, null, or undefined) with TypeScript type guard support.

import { isPrimitive } from '@ffsm/serialize';

// Basic usage
const value = getSomeValue();
if (isPrimitive(value)) {
  // TypeScript knows value is a string, number, boolean, null, or undefined here
  console.log(`Simple value: ${String(value)}`);
} else {
  // TypeScript knows value is an object or array here
  console.log('Complex data structure');
}

// Differentiating between primitive and complex values
function serializeValue(value: unknown) {
  if (isPrimitive(value)) {
    return String(value); // Simple stringification for primitives
  } else {
    return JSON.stringify(value); // JSON for objects and arrays
  }
}

// Validating input types
function validateInput(input: unknown) {
  if (!isPrimitive(input)) {
    throw new Error('Only primitive values are accepted');
  }
  return input; // TypeScript knows input is a primitive here
}

This function helps distinguish between simple primitive values and complex data structures (objects and arrays) both at runtime and at compile time with TypeScript.