vocal-stack 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +573 -59
- package/dist/index.cjs +57 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +57 -28
- package/dist/index.js.map +1 -1
- package/dist/sanitizer/index.cjs +57 -28
- package/dist/sanitizer/index.cjs.map +1 -1
- package/dist/sanitizer/index.d.cts +16 -0
- package/dist/sanitizer/index.d.ts +16 -0
- package/dist/sanitizer/index.js +57 -28
- package/dist/sanitizer/index.js.map +1 -1
- package/package.json +15 -15
package/dist/sanitizer/index.js
CHANGED
|
@@ -92,6 +92,58 @@ var SpeechSanitizer = class {
|
|
|
92
92
|
};
|
|
93
93
|
this.plugins = [...this.config.plugins].sort((a, b) => a.priority - b.priority);
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Apply built-in sanitization rules
|
|
97
|
+
*/
|
|
98
|
+
applyRules(text) {
|
|
99
|
+
let result = text;
|
|
100
|
+
for (const rule of this.config.rules) {
|
|
101
|
+
const ruleFunction = ruleRegistry.get(rule);
|
|
102
|
+
if (ruleFunction) {
|
|
103
|
+
result = ruleFunction(result, this.config);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Apply plugin transformations
|
|
110
|
+
*/
|
|
111
|
+
applyPlugins(text) {
|
|
112
|
+
let result = text;
|
|
113
|
+
for (const plugin of this.plugins) {
|
|
114
|
+
const transformed = plugin.transform(result);
|
|
115
|
+
if (transformed instanceof Promise) {
|
|
116
|
+
throw new SanitizerError(
|
|
117
|
+
`Plugin ${plugin.name} returned a Promise in sync sanitize(). Use sanitizeAsync() instead.`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
result = transformed;
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Apply custom replacements
|
|
126
|
+
*/
|
|
127
|
+
applyCustomReplacements(text) {
|
|
128
|
+
let result = text;
|
|
129
|
+
for (const [pattern, replacement] of this.config.customReplacements) {
|
|
130
|
+
if (typeof pattern === "string") {
|
|
131
|
+
result = result.replaceAll(pattern, replacement);
|
|
132
|
+
} else {
|
|
133
|
+
result = result.replace(pattern, replacement);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Clean up whitespace according to configuration
|
|
140
|
+
*/
|
|
141
|
+
cleanupWhitespace(text) {
|
|
142
|
+
if (this.config.preserveLineBreaks) {
|
|
143
|
+
return text.replace(/ +/g, " ").replace(/^\s+|\s+$/gm, "");
|
|
144
|
+
}
|
|
145
|
+
return text.replace(/\s+/g, " ").trim();
|
|
146
|
+
}
|
|
95
147
|
/**
|
|
96
148
|
* Sanitize a string synchronously
|
|
97
149
|
*/
|
|
@@ -99,35 +151,12 @@ var SpeechSanitizer = class {
|
|
|
99
151
|
if (!text || text.trim().length === 0) {
|
|
100
152
|
return "";
|
|
101
153
|
}
|
|
102
|
-
let result = text;
|
|
103
154
|
try {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
for (const plugin of this.plugins) {
|
|
111
|
-
const transformed = plugin.transform(result);
|
|
112
|
-
if (transformed instanceof Promise) {
|
|
113
|
-
throw new SanitizerError(
|
|
114
|
-
`Plugin ${plugin.name} returned a Promise in sync sanitize(). Use sanitizeAsync() instead.`
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
result = transformed;
|
|
118
|
-
}
|
|
119
|
-
for (const [pattern, replacement] of this.config.customReplacements) {
|
|
120
|
-
if (typeof pattern === "string") {
|
|
121
|
-
result = result.replaceAll(pattern, replacement);
|
|
122
|
-
} else {
|
|
123
|
-
result = result.replace(pattern, replacement);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
if (this.config.preserveLineBreaks) {
|
|
127
|
-
result = result.replace(/ +/g, " ").replace(/^\s+|\s+$/gm, "");
|
|
128
|
-
} else {
|
|
129
|
-
result = result.replace(/\s+/g, " ").trim();
|
|
130
|
-
}
|
|
155
|
+
let result = text;
|
|
156
|
+
result = this.applyRules(result);
|
|
157
|
+
result = this.applyPlugins(result);
|
|
158
|
+
result = this.applyCustomReplacements(result);
|
|
159
|
+
result = this.cleanupWhitespace(result);
|
|
131
160
|
return result;
|
|
132
161
|
} catch (error) {
|
|
133
162
|
if (error instanceof SanitizerError) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/errors.ts","../../src/sanitizer/rules/code-blocks.ts","../../src/sanitizer/rules/markdown.ts","../../src/sanitizer/rules/punctuation.ts","../../src/sanitizer/rules/urls.ts","../../src/sanitizer/rules/index.ts","../../src/sanitizer/sanitizer.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Base error class for all vocal-stack errors\n */\nexport class VocalStackError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly context?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'VocalStackError';\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\n/**\n * Error thrown during text sanitization\n */\nexport class SanitizerError extends VocalStackError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'SANITIZER_ERROR', context);\n this.name = 'SanitizerError';\n }\n}\n\n/**\n * Error thrown during flow control operations\n */\nexport class FlowControlError extends VocalStackError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'FLOW_CONTROL_ERROR', context);\n this.name = 'FlowControlError';\n }\n}\n\n/**\n * Error thrown during monitoring operations\n */\nexport class MonitorError extends VocalStackError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'MONITOR_ERROR', context);\n this.name = 'MonitorError';\n }\n}\n","import type { SanitizerConfig } from '../types';\n\n/**\n * Remove code blocks and inline code to make text speakable\n */\nexport function codeBlocksRule(text: string, _config: Required<SanitizerConfig>): string {\n let result = text;\n\n // Remove fenced code blocks (```code```)\n // Match both with and without language specifier\n result = result.replace(/```[\\s\\S]*?```/g, '');\n\n // Remove indented code blocks (4 spaces or 1 tab at line start)\n result = result.replace(/^(?: {4}|\\t).+$/gm, '');\n\n // Note: Inline code (`code`) is handled by markdown rule\n\n return result;\n}\n","import type { SanitizerConfig } from '../types';\n\n/**\n * Strip markdown syntax to make text speakable\n */\nexport function markdownRule(text: string, _config: Required<SanitizerConfig>): string {\n let result = text;\n\n // Remove headers (##, ###, etc.)\n result = result.replace(/^#{1,6}\\s+/gm, '');\n\n // Remove bold/italic (**text**, *text*, __text__, _text_)\n result = result.replace(/(\\*\\*|__)(.*?)\\1/g, '$2');\n result = result.replace(/(\\*|_)(.*?)\\1/g, '$2');\n\n // Remove inline code (`code`)\n result = result.replace(/`([^`]+)`/g, '$1');\n\n // Remove links but keep text [text](url) -> text\n result = result.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1');\n\n // Remove images  -> alt or empty\n result = result.replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, '$1');\n\n // Remove blockquotes (>)\n result = result.replace(/^>\\s+/gm, '');\n\n // Remove horizontal rules (---, ***, ___)\n result = result.replace(/^[\\-*_]{3,}$/gm, '');\n\n // Remove list markers (-, *, 1., etc.)\n result = result.replace(/^[\\s]*[-*+]\\s+/gm, '');\n result = result.replace(/^[\\s]*\\d+\\.\\s+/gm, '');\n\n // Remove strikethrough (~~text~~)\n result = result.replace(/~~(.*?)~~/g, '$1');\n\n // Remove task list markers (- [ ], - [x])\n result = result.replace(/^[\\s]*-\\s+\\[[ xX]\\]\\s+/gm, '');\n\n return result;\n}\n","import type { SanitizerConfig } from '../types';\n\n/**\n * Normalize punctuation for better TTS handling\n */\nexport function punctuationRule(text: string, _config: Required<SanitizerConfig>): string {\n let result = text;\n\n // Replace multiple exclamation marks with single\n result = result.replace(/!{2,}/g, '!');\n\n // Replace multiple question marks with single\n result = result.replace(/\\?{2,}/g, '?');\n\n // Replace ellipsis variations with single space or period\n result = result.replace(/\\.{3,}/g, '.');\n\n // Remove multiple dashes/hyphens\n result = result.replace(/-{2,}/g, ' ');\n\n // Replace em dash and en dash with space\n result = result.replace(/[—–]/g, ' ');\n\n // Remove parentheses and brackets but keep content\n result = result.replace(/[()[\\]{}]/g, '');\n\n // Replace multiple commas with single\n result = result.replace(/,{2,}/g, ',');\n\n // Remove semicolons and colons (can be disruptive in speech)\n result = result.replace(/[;:]/g, ',');\n\n // Remove quotation marks\n result = result.replace(/[\"'\"'`]/g, '');\n\n // Remove asterisks and underscores (from markdown remnants)\n result = result.replace(/[*_]/g, '');\n\n // Remove forward slashes and backslashes\n result = result.replace(/[/\\\\]/g, ' ');\n\n // Remove pipes and ampersands\n result = result.replace(/[|&]/g, ' ');\n\n // Remove @ # $ % symbols\n result = result.replace(/[@#$%]/g, '');\n\n return result;\n}\n","import type { SanitizerConfig } from '../types';\n\n/**\n * Remove or replace URLs to make text speakable\n */\nexport function urlsRule(text: string, _config: Required<SanitizerConfig>): string {\n let result = text;\n\n // Match URLs with protocol (http://, https://, ftp://, etc.)\n const urlWithProtocol = /\\b(https?:\\/\\/|ftp:\\/\\/|www\\.)[^\\s<>\"{}|\\\\^`[\\]]+/gi;\n\n // Match email addresses\n const emailPattern = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b/g;\n\n // Remove URLs with protocol\n result = result.replace(urlWithProtocol, '');\n\n // Remove email addresses\n result = result.replace(emailPattern, '');\n\n // Remove standalone domain-like patterns (example.com)\n result = result.replace(/\\b[a-z0-9-]+\\.[a-z]{2,}\\b/gi, '');\n\n return result;\n}\n","import type { RuleFunction } from '../types';\nimport { codeBlocksRule } from './code-blocks';\nimport { markdownRule } from './markdown';\nimport { punctuationRule } from './punctuation';\nimport { urlsRule } from './urls';\n\n/**\n * Registry of built-in sanitization rules\n */\nexport const ruleRegistry = new Map<string, RuleFunction>([\n ['markdown', markdownRule],\n ['urls', urlsRule],\n ['code-blocks', codeBlocksRule],\n ['punctuation', punctuationRule],\n]);\n","import { SanitizerError } from '../errors';\nimport { ruleRegistry } from './rules';\nimport type { SanitizationResult, SanitizerConfig, SanitizerPlugin } from './types';\n\n/**\n * Speech sanitizer that transforms text for TTS optimization\n */\nexport class SpeechSanitizer {\n private readonly config: Required<SanitizerConfig>;\n private readonly plugins: readonly SanitizerPlugin[];\n\n constructor(config: SanitizerConfig = {}) {\n this.config = {\n rules: config.rules ?? ['markdown', 'urls', 'code-blocks', 'punctuation'],\n plugins: config.plugins ?? [],\n preserveLineBreaks: config.preserveLineBreaks ?? false,\n customReplacements: config.customReplacements ?? new Map(),\n };\n this.plugins = [...this.config.plugins].sort((a, b) => a.priority - b.priority);\n }\n\n /**\n * Sanitize a string synchronously\n */\n sanitize(text: string): string {\n if (!text || text.trim().length === 0) {\n return '';\n }\n\n let result = text;\n\n try {\n // Apply built-in rules\n for (const rule of this.config.rules) {\n const ruleFunction = ruleRegistry.get(rule);\n if (ruleFunction) {\n result = ruleFunction(result, this.config);\n }\n }\n\n // Apply plugins (sync only in this version)\n for (const plugin of this.plugins) {\n const transformed = plugin.transform(result);\n if (transformed instanceof Promise) {\n throw new SanitizerError(\n `Plugin ${plugin.name} returned a Promise in sync sanitize(). Use sanitizeAsync() instead.`\n );\n }\n result = transformed;\n }\n\n // Apply custom replacements\n for (const [pattern, replacement] of this.config.customReplacements) {\n if (typeof pattern === 'string') {\n result = result.replaceAll(pattern, replacement);\n } else {\n result = result.replace(pattern, replacement);\n }\n }\n\n // Clean up whitespace\n if (this.config.preserveLineBreaks) {\n // Collapse multiple spaces on same line but keep line breaks\n result = result.replace(/ +/g, ' ').replace(/^\\s+|\\s+$/gm, '');\n } else {\n // Collapse all whitespace including line breaks\n result = result.replace(/\\s+/g, ' ').trim();\n }\n\n return result;\n } catch (error) {\n if (error instanceof SanitizerError) {\n throw error;\n }\n throw new SanitizerError('Failed to sanitize text', {\n originalText: text.substring(0, 100),\n error,\n });\n }\n }\n\n /**\n * Sanitize with detailed result metadata\n */\n sanitizeWithMetadata(text: string): SanitizationResult {\n const original = text;\n const sanitized = this.sanitize(text);\n\n return {\n original,\n sanitized,\n appliedRules: this.config.rules as string[],\n metadata: {\n removedCount: original.length - sanitized.length,\n transformedCount: this.config.rules.length + this.plugins.length,\n },\n };\n }\n\n /**\n * Sanitize a stream of text chunks (AsyncIterable)\n */\n async *sanitizeStream(input: AsyncIterable<string>): AsyncIterable<string> {\n let buffer = '';\n const sentenceBoundary = /[.!?]\\s+/;\n\n for await (const chunk of input) {\n buffer += chunk;\n\n // Process complete sentences\n const sentences = buffer.split(sentenceBoundary);\n buffer = sentences.pop() ?? ''; // Keep incomplete sentence\n\n for (const sentence of sentences) {\n if (sentence.trim()) {\n yield `${this.sanitize(sentence)} `;\n }\n }\n }\n\n // Process remaining buffer\n if (buffer.trim()) {\n yield this.sanitize(buffer);\n }\n }\n}\n\n/**\n * Convenience function for one-off sanitization\n */\nexport function sanitizeForSpeech(text: string, config?: SanitizerConfig): string {\n const sanitizer = new SpeechSanitizer(config);\n return sanitizer.sanitize(text);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/sanitizer/rules/code-blocks.ts","../../src/sanitizer/rules/markdown.ts","../../src/sanitizer/rules/punctuation.ts","../../src/sanitizer/rules/urls.ts","../../src/sanitizer/rules/index.ts","../../src/sanitizer/sanitizer.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Base error class for all vocal-stack errors\n */\nexport class VocalStackError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly context?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'VocalStackError';\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\n/**\n * Error thrown during text sanitization\n */\nexport class SanitizerError extends VocalStackError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'SANITIZER_ERROR', context);\n this.name = 'SanitizerError';\n }\n}\n\n/**\n * Error thrown during flow control operations\n */\nexport class FlowControlError extends VocalStackError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'FLOW_CONTROL_ERROR', context);\n this.name = 'FlowControlError';\n }\n}\n\n/**\n * Error thrown during monitoring operations\n */\nexport class MonitorError extends VocalStackError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'MONITOR_ERROR', context);\n this.name = 'MonitorError';\n }\n}\n","import type { SanitizerConfig } from '../types';\n\n/**\n * Remove code blocks and inline code to make text speakable\n */\nexport function codeBlocksRule(text: string, _config: Required<SanitizerConfig>): string {\n let result = text;\n\n // Remove fenced code blocks (```code```)\n // Match both with and without language specifier\n result = result.replace(/```[\\s\\S]*?```/g, '');\n\n // Remove indented code blocks (4 spaces or 1 tab at line start)\n result = result.replace(/^(?: {4}|\\t).+$/gm, '');\n\n // Note: Inline code (`code`) is handled by markdown rule\n\n return result;\n}\n","import type { SanitizerConfig } from '../types';\n\n/**\n * Strip markdown syntax to make text speakable\n */\nexport function markdownRule(text: string, _config: Required<SanitizerConfig>): string {\n let result = text;\n\n // Remove headers (##, ###, etc.)\n result = result.replace(/^#{1,6}\\s+/gm, '');\n\n // Remove bold/italic (**text**, *text*, __text__, _text_)\n result = result.replace(/(\\*\\*|__)(.*?)\\1/g, '$2');\n result = result.replace(/(\\*|_)(.*?)\\1/g, '$2');\n\n // Remove inline code (`code`)\n result = result.replace(/`([^`]+)`/g, '$1');\n\n // Remove links but keep text [text](url) -> text\n result = result.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1');\n\n // Remove images  -> alt or empty\n result = result.replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, '$1');\n\n // Remove blockquotes (>)\n result = result.replace(/^>\\s+/gm, '');\n\n // Remove horizontal rules (---, ***, ___)\n result = result.replace(/^[\\-*_]{3,}$/gm, '');\n\n // Remove list markers (-, *, 1., etc.)\n result = result.replace(/^[\\s]*[-*+]\\s+/gm, '');\n result = result.replace(/^[\\s]*\\d+\\.\\s+/gm, '');\n\n // Remove strikethrough (~~text~~)\n result = result.replace(/~~(.*?)~~/g, '$1');\n\n // Remove task list markers (- [ ], - [x])\n result = result.replace(/^[\\s]*-\\s+\\[[ xX]\\]\\s+/gm, '');\n\n return result;\n}\n","import type { SanitizerConfig } from '../types';\n\n/**\n * Normalize punctuation for better TTS handling\n */\nexport function punctuationRule(text: string, _config: Required<SanitizerConfig>): string {\n let result = text;\n\n // Replace multiple exclamation marks with single\n result = result.replace(/!{2,}/g, '!');\n\n // Replace multiple question marks with single\n result = result.replace(/\\?{2,}/g, '?');\n\n // Replace ellipsis variations with single space or period\n result = result.replace(/\\.{3,}/g, '.');\n\n // Remove multiple dashes/hyphens\n result = result.replace(/-{2,}/g, ' ');\n\n // Replace em dash and en dash with space\n result = result.replace(/[—–]/g, ' ');\n\n // Remove parentheses and brackets but keep content\n result = result.replace(/[()[\\]{}]/g, '');\n\n // Replace multiple commas with single\n result = result.replace(/,{2,}/g, ',');\n\n // Remove semicolons and colons (can be disruptive in speech)\n result = result.replace(/[;:]/g, ',');\n\n // Remove quotation marks\n result = result.replace(/[\"'\"'`]/g, '');\n\n // Remove asterisks and underscores (from markdown remnants)\n result = result.replace(/[*_]/g, '');\n\n // Remove forward slashes and backslashes\n result = result.replace(/[/\\\\]/g, ' ');\n\n // Remove pipes and ampersands\n result = result.replace(/[|&]/g, ' ');\n\n // Remove @ # $ % symbols\n result = result.replace(/[@#$%]/g, '');\n\n return result;\n}\n","import type { SanitizerConfig } from '../types';\n\n/**\n * Remove or replace URLs to make text speakable\n */\nexport function urlsRule(text: string, _config: Required<SanitizerConfig>): string {\n let result = text;\n\n // Match URLs with protocol (http://, https://, ftp://, etc.)\n const urlWithProtocol = /\\b(https?:\\/\\/|ftp:\\/\\/|www\\.)[^\\s<>\"{}|\\\\^`[\\]]+/gi;\n\n // Match email addresses\n const emailPattern = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b/g;\n\n // Remove URLs with protocol\n result = result.replace(urlWithProtocol, '');\n\n // Remove email addresses\n result = result.replace(emailPattern, '');\n\n // Remove standalone domain-like patterns (example.com)\n result = result.replace(/\\b[a-z0-9-]+\\.[a-z]{2,}\\b/gi, '');\n\n return result;\n}\n","import type { RuleFunction } from '../types';\nimport { codeBlocksRule } from './code-blocks';\nimport { markdownRule } from './markdown';\nimport { punctuationRule } from './punctuation';\nimport { urlsRule } from './urls';\n\n/**\n * Registry of built-in sanitization rules\n */\nexport const ruleRegistry = new Map<string, RuleFunction>([\n ['markdown', markdownRule],\n ['urls', urlsRule],\n ['code-blocks', codeBlocksRule],\n ['punctuation', punctuationRule],\n]);\n","import { SanitizerError } from '../errors';\nimport { ruleRegistry } from './rules';\nimport type { SanitizationResult, SanitizerConfig, SanitizerPlugin } from './types';\n\n/**\n * Speech sanitizer that transforms text for TTS optimization\n */\nexport class SpeechSanitizer {\n private readonly config: Required<SanitizerConfig>;\n private readonly plugins: readonly SanitizerPlugin[];\n\n constructor(config: SanitizerConfig = {}) {\n this.config = {\n rules: config.rules ?? ['markdown', 'urls', 'code-blocks', 'punctuation'],\n plugins: config.plugins ?? [],\n preserveLineBreaks: config.preserveLineBreaks ?? false,\n customReplacements: config.customReplacements ?? new Map(),\n };\n this.plugins = [...this.config.plugins].sort((a, b) => a.priority - b.priority);\n }\n\n /**\n * Apply built-in sanitization rules\n */\n private applyRules(text: string): string {\n let result = text;\n for (const rule of this.config.rules) {\n const ruleFunction = ruleRegistry.get(rule);\n if (ruleFunction) {\n result = ruleFunction(result, this.config);\n }\n }\n return result;\n }\n\n /**\n * Apply plugin transformations\n */\n private applyPlugins(text: string): string {\n let result = text;\n for (const plugin of this.plugins) {\n const transformed = plugin.transform(result);\n if (transformed instanceof Promise) {\n throw new SanitizerError(\n `Plugin ${plugin.name} returned a Promise in sync sanitize(). Use sanitizeAsync() instead.`\n );\n }\n result = transformed;\n }\n return result;\n }\n\n /**\n * Apply custom replacements\n */\n private applyCustomReplacements(text: string): string {\n let result = text;\n for (const [pattern, replacement] of this.config.customReplacements) {\n if (typeof pattern === 'string') {\n result = result.replaceAll(pattern, replacement);\n } else {\n result = result.replace(pattern, replacement);\n }\n }\n return result;\n }\n\n /**\n * Clean up whitespace according to configuration\n */\n private cleanupWhitespace(text: string): string {\n if (this.config.preserveLineBreaks) {\n // Collapse multiple spaces on same line but keep line breaks\n return text.replace(/ +/g, ' ').replace(/^\\s+|\\s+$/gm, '');\n }\n // Collapse all whitespace including line breaks\n return text.replace(/\\s+/g, ' ').trim();\n }\n\n /**\n * Sanitize a string synchronously\n */\n sanitize(text: string): string {\n if (!text || text.trim().length === 0) {\n return '';\n }\n\n try {\n let result = text;\n result = this.applyRules(result);\n result = this.applyPlugins(result);\n result = this.applyCustomReplacements(result);\n result = this.cleanupWhitespace(result);\n return result;\n } catch (error) {\n if (error instanceof SanitizerError) {\n throw error;\n }\n throw new SanitizerError('Failed to sanitize text', {\n originalText: text.substring(0, 100),\n error,\n });\n }\n }\n\n /**\n * Sanitize with detailed result metadata\n */\n sanitizeWithMetadata(text: string): SanitizationResult {\n const original = text;\n const sanitized = this.sanitize(text);\n\n return {\n original,\n sanitized,\n appliedRules: this.config.rules as string[],\n metadata: {\n removedCount: original.length - sanitized.length,\n transformedCount: this.config.rules.length + this.plugins.length,\n },\n };\n }\n\n /**\n * Sanitize a stream of text chunks (AsyncIterable)\n */\n async *sanitizeStream(input: AsyncIterable<string>): AsyncIterable<string> {\n let buffer = '';\n const sentenceBoundary = /[.!?]\\s+/;\n\n for await (const chunk of input) {\n buffer += chunk;\n\n // Process complete sentences\n const sentences = buffer.split(sentenceBoundary);\n buffer = sentences.pop() ?? ''; // Keep incomplete sentence\n\n for (const sentence of sentences) {\n if (sentence.trim()) {\n yield `${this.sanitize(sentence)} `;\n }\n }\n }\n\n // Process remaining buffer\n if (buffer.trim()) {\n yield this.sanitize(buffer);\n }\n }\n}\n\n/**\n * Convenience function for one-off sanitization\n */\nexport function sanitizeForSpeech(text: string, config?: SanitizerConfig): string {\n const sanitizer = new SpeechSanitizer(config);\n return sanitizer.sanitize(text);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vocal-stack",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "High-performance utility library for Voice AI agents - text sanitization, flow control, and latency monitoring",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
|
-
"import": "./dist/index.
|
|
10
|
-
"require": "./dist/index.
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"require": "./dist/index.cjs"
|
|
11
11
|
},
|
|
12
12
|
"./sanitizer": {
|
|
13
13
|
"types": "./dist/sanitizer/index.d.ts",
|
|
14
|
-
"import": "./dist/sanitizer/index.
|
|
15
|
-
"require": "./dist/sanitizer/index.
|
|
14
|
+
"import": "./dist/sanitizer/index.js",
|
|
15
|
+
"require": "./dist/sanitizer/index.cjs"
|
|
16
16
|
},
|
|
17
17
|
"./flow": {
|
|
18
18
|
"types": "./dist/flow/index.d.ts",
|
|
19
|
-
"import": "./dist/flow/index.
|
|
20
|
-
"require": "./dist/flow/index.
|
|
19
|
+
"import": "./dist/flow/index.js",
|
|
20
|
+
"require": "./dist/flow/index.cjs"
|
|
21
21
|
},
|
|
22
22
|
"./monitor": {
|
|
23
23
|
"types": "./dist/monitor/index.d.ts",
|
|
24
|
-
"import": "./dist/monitor/index.
|
|
25
|
-
"require": "./dist/monitor/index.
|
|
24
|
+
"import": "./dist/monitor/index.js",
|
|
25
|
+
"require": "./dist/monitor/index.cjs"
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
|
-
"main": "./dist/index.
|
|
29
|
-
"module": "./dist/index.
|
|
28
|
+
"main": "./dist/index.cjs",
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
30
|
"types": "./dist/index.d.ts",
|
|
31
31
|
"files": [
|
|
32
32
|
"dist",
|
|
@@ -63,16 +63,16 @@
|
|
|
63
63
|
"openai",
|
|
64
64
|
"elevenlabs"
|
|
65
65
|
],
|
|
66
|
-
"author": "",
|
|
66
|
+
"author": "Gaurav <gauravchaulagain0@gmail.com>",
|
|
67
67
|
"license": "MIT",
|
|
68
68
|
"repository": {
|
|
69
69
|
"type": "git",
|
|
70
|
-
"url": ""
|
|
70
|
+
"url": "https://github.com/gaurav890/vocal-stack"
|
|
71
71
|
},
|
|
72
72
|
"bugs": {
|
|
73
|
-
"url": ""
|
|
73
|
+
"url": "https://github.com/gaurav890/vocal-stack/issues"
|
|
74
74
|
},
|
|
75
|
-
"homepage": "",
|
|
75
|
+
"homepage": "https://github.com/gaurav890/vocal-stack#readme",
|
|
76
76
|
"engines": {
|
|
77
77
|
"node": ">=18.0.0"
|
|
78
78
|
},
|