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

@bernierllc/content-soft-delete

v1.2.0

Published

Pure utility for soft delete functionality - mark content as deleted without permanent removal, track deletion metadata, and support restoration

Readme

@bernierllc/content-soft-delete

Pure utility for soft delete functionality - mark content as deleted without permanent removal, track deletion metadata, and support restoration.

Installation

npm install @bernierllc/content-soft-delete

Usage

Basic Usage

import { ContentSoftDelete, createSoftDelete } from '@bernierllc/content-soft-delete';

// Create instance with default configuration
const softDelete = new ContentSoftDelete();

// Or use factory function
const softDelete = createSoftDelete({ retentionDays: 60 });

// Mark content as soft-deleted
const content = { id: 'article-123', title: 'My Article', author: 'John Doe' };
const result = softDelete.mark(content, 'user-456', 'No longer relevant');

if (result.success) {
  console.log('Content marked as deleted:', result.data);
  // { id: 'article-123', title: 'My Article', author: 'John Doe', softDelete: {...} }
}

// Restore soft-deleted content
const restored = softDelete.restore(result.data);
if (restored.success) {
  console.log('Content restored:', restored.data);
  // { id: 'article-123', title: 'My Article', author: 'John Doe' }
}

Configuration Options

import { ContentSoftDelete, SoftDeleteConfig } from '@bernierllc/content-soft-delete';

const config: Partial<SoftDeleteConfig> = {
  retentionDays: 30,      // Days before permanent deletion (default: 30)
  allowRestore: true,     // Whether restoration is permitted (default: true)
  requireReason: false    // Whether delete reason is required (default: false)
};

const softDelete = new ContentSoftDelete(config);

Filtering Content

const items = [
  { id: '1', title: 'Active 1' },
  { id: '2', title: 'Deleted', softDelete: {...} },
  { id: '3', title: 'Active 2' }
];

// Get only active content
const active = softDelete.filterActive(items);
// Returns: [{ id: '1', ... }, { id: '3', ... }]

// Get only deleted content
const deleted = softDelete.filterDeleted(items);
// Returns: [{ id: '2', ... }]

Checking Deletion Status

// Check if content is soft-deleted
const isDeleted = softDelete.isSoftDeleted(content);

// Check if content is ready for permanent deletion
const canDelete = softDelete.isPermanentlyDeletable(content);

// Get days remaining until permanent deletion
const days = softDelete.getDaysUntilPermanentDelete(content);
console.log(`${days} days until permanent deletion`);

Permanent Deletion

// Prepare content for permanent deletion
const result = softDelete.preparePermanentDelete(content);

if (result.success) {
  // Only the ID remains for audit purposes
  console.log(result.data); // { id: 'article-123' }

  // Now you can permanently delete from your database
  await database.delete(result.data.id);
}

API Reference

Classes

ContentSoftDelete

Main class for soft delete operations.

Constructor:

new ContentSoftDelete(config?: Partial<SoftDeleteConfig>)

Methods:

  • mark<T>(content: T, userId: string, reason?: string): SoftDeleteResult<T>

    • Marks content as soft-deleted with metadata
    • Returns success status and updated content with softDelete metadata
  • restore<T>(content: T): SoftDeleteResult<T>

    • Restores soft-deleted content by removing softDelete metadata
    • Returns success status and restored content
  • isSoftDeleted(content: SoftDeletableContent): boolean

    • Checks if content is currently soft-deleted
    • Returns true if softDelete metadata exists
  • isPermanentlyDeletable(content: SoftDeletableContent): boolean

    • Checks if content is ready for permanent deletion
    • Returns true if retention period has expired
  • filterActive<T>(items: T[]): T[]

    • Filters array to exclude soft-deleted items
    • Returns only active content
  • filterDeleted<T>(items: T[]): T[]

    • Filters array to include only soft-deleted items
    • Returns only deleted content
  • getDaysUntilPermanentDelete(content: SoftDeletableContent): number | null

    • Calculates days remaining until permanent deletion
    • Returns number of days or null if not applicable
  • preparePermanentDelete<T>(content: T): SoftDeleteResult<{ id: string }>

    • Prepares content for permanent deletion (returns only ID)
    • Returns success status and minimal data for audit

Interfaces

SoftDeleteConfig

Configuration for soft delete behavior.

interface SoftDeleteConfig {
  retentionDays: number;    // Days before permanent deletion (default: 30)
  allowRestore: boolean;    // Whether restoration is permitted (default: true)
  requireReason: boolean;   // Whether delete reason is required (default: false)
}

SoftDeleteMetadata

Metadata attached to soft-deleted content.

interface SoftDeleteMetadata {
  deletedAt: string;          // ISO 8601 timestamp
  deletedBy: string;          // User ID who deleted
  deleteReason?: string;      // Optional reason
  canRestore: boolean;        // Whether restoration is allowed
  permanentDeleteAt?: string; // When permanent deletion will occur
}

SoftDeletableContent

Content item with soft delete support.

interface SoftDeletableContent {
  id: string;
  softDelete?: SoftDeleteMetadata;
  [key: string]: any; // Other content properties
}

SoftDeleteResult<T>

Result of soft delete operations.

interface SoftDeleteResult<T = SoftDeletableContent> {
  success: boolean;
  data?: T;
  error?: string;
}

Factory Function

createSoftDelete(config?: Partial<SoftDeleteConfig>): ContentSoftDelete

Convenience factory function for creating ContentSoftDelete instances.

const softDelete = createSoftDelete({ retentionDays: 60 });

Examples

Example 1: Editorial Workflow

import { createSoftDelete } from '@bernierllc/content-soft-delete';

const softDelete = createSoftDelete({
  retentionDays: 30,
  requireReason: true
});

// Editor marks article for deletion
const article = { id: 'article-123', title: 'Draft Article', status: 'draft' };
const result = softDelete.mark(article, 'editor-456', 'Superseded by new version');

if (result.success) {
  // Save to database with softDelete metadata
  await database.save(result.data);

  // Check when it will be permanently deleted
  const days = softDelete.getDaysUntilPermanentDelete(result.data);
  console.log(`Article will be permanently deleted in ${days} days`);
}

// Later, restore if needed
const restored = softDelete.restore(result.data);
if (restored.success) {
  await database.save(restored.data);
  console.log('Article restored to active status');
}

Example 2: Filtering Content Lists

import { ContentSoftDelete } from '@bernierllc/content-soft-delete';

const softDelete = new ContentSoftDelete();

// Fetch all content from database
const allContent = await database.getAllContent();

// Show only active content to users
const activeContent = softDelete.filterActive(allContent);
displayToUser(activeContent);

// Admin view: show trash
const deletedContent = softDelete.filterDeleted(allContent);
displayTrash(deletedContent);

Example 3: Automated Cleanup

import { ContentSoftDelete } from '@bernierllc/content-soft-delete';

const softDelete = new ContentSoftDelete({ retentionDays: 30 });

async function cleanupExpiredContent() {
  const allContent = await database.getAllContent();
  const deleted = softDelete.filterDeleted(allContent);

  for (const item of deleted) {
    if (softDelete.isPermanentlyDeletable(item)) {
      const prepared = softDelete.preparePermanentDelete(item);
      if (prepared.success) {
        // Permanently delete from database
        await database.permanentDelete(prepared.data.id);
        console.log(`Permanently deleted: ${prepared.data.id}`);
      }
    }
  }
}

// Run daily cleanup
setInterval(cleanupExpiredContent, 24 * 60 * 60 * 1000);

Example 4: Custom Retention Policies

import { createSoftDelete } from '@bernierllc/content-soft-delete';

// Different retention periods for different content types
const articleSoftDelete = createSoftDelete({ retentionDays: 90 });
const commentSoftDelete = createSoftDelete({ retentionDays: 30 });
const draftSoftDelete = createSoftDelete({ retentionDays: 7 });

// Articles get 90 days
const deletedArticle = articleSoftDelete.mark(article, userId, 'Outdated');

// Comments get 30 days
const deletedComment = commentSoftDelete.mark(comment, userId, 'Spam');

// Drafts get only 7 days
const deletedDraft = draftSoftDelete.mark(draft, userId, 'Abandoned');

Integration Status

  • Logger: not-applicable (Pure utility with no side effects - logging is caller's responsibility)
  • Docs-Suite: ready (Complete TypeDoc documentation)
  • NeverHub: not-applicable (Pure utility with no runtime behavior - no events to publish)

Design Philosophy

This package follows MECE (Mutually Exclusive, Collectively Exhaustive) principles:

What it does:

  • ✅ Mark content as soft-deleted with metadata
  • ✅ Track deletion timestamp, user ID, and reason
  • ✅ Restore soft-deleted content
  • ✅ Filter arrays to exclude/include soft-deleted items
  • ✅ Check if content is permanently deletable
  • ✅ Calculate retention periods
  • ✅ Validate deletion state transitions

What it does NOT do:

  • ❌ Persist deletion state to database (caller's responsibility)
  • ❌ Handle authorization/permissions
  • ❌ Send notifications about deletions
  • ❌ Archive or backup deleted content
  • ❌ Manage content versions or history

Dependencies

None - This is a pure utility package with zero dependencies.

License

Copyright (c) 2025 Bernier LLC

This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.

See Also