feat(core): Introduce native implementations for Base64, random generation and normalization; remove runtime plugin dependencies; update tests, docs and package metadata
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @pushrocks/commitinfo
|
||||
* autocreated commitinfo by @push.rocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartstring',
|
||||
version: '4.0.15',
|
||||
description: 'handle strings in smart ways. TypeScript ready.'
|
||||
version: '4.1.0',
|
||||
description: 'A library for handling strings in smart ways, including manipulation and encoding, with TypeScript support.'
|
||||
}
|
||||
|
@@ -1,10 +1,89 @@
|
||||
import * as plugins from './smartstring.plugins.js';
|
||||
|
||||
/**
|
||||
* the type for base 64
|
||||
*/
|
||||
export type TStringInputType = 'string' | 'base64' | 'base64uri';
|
||||
|
||||
/**
|
||||
* Cross-platform base64 implementation
|
||||
* Works in both Node.js and browser environments
|
||||
*/
|
||||
const universalBase64 = {
|
||||
encode: (str: string): string => {
|
||||
if (typeof Buffer !== 'undefined') {
|
||||
// Node.js environment
|
||||
return Buffer.from(str, 'utf8').toString('base64');
|
||||
} else if (typeof btoa !== 'undefined') {
|
||||
// Browser environment
|
||||
// Handle Unicode properly
|
||||
const utf8Bytes = new TextEncoder().encode(str);
|
||||
const binaryString = Array.from(utf8Bytes, byte => String.fromCharCode(byte)).join('');
|
||||
return btoa(binaryString);
|
||||
} else {
|
||||
// Fallback pure JS implementation
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
const bytes = new TextEncoder().encode(str);
|
||||
let result = '';
|
||||
let i = 0;
|
||||
|
||||
while (i < bytes.length) {
|
||||
const a = bytes[i++];
|
||||
const b = i < bytes.length ? bytes[i++] : 0;
|
||||
const c = i < bytes.length ? bytes[i++] : 0;
|
||||
|
||||
const bitmap = (a << 16) | (b << 8) | c;
|
||||
|
||||
result += chars.charAt((bitmap >> 18) & 63);
|
||||
result += chars.charAt((bitmap >> 12) & 63);
|
||||
result += i - 2 < bytes.length ? chars.charAt((bitmap >> 6) & 63) : '=';
|
||||
result += i - 1 < bytes.length ? chars.charAt(bitmap & 63) : '=';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
|
||||
decode: (str: string): string => {
|
||||
// Handle base64uri by converting back to standard base64
|
||||
const base64String = str
|
||||
.replace(/-/g, '+')
|
||||
.replace(/_/g, '/')
|
||||
.padEnd(str.length + ((4 - (str.length % 4)) % 4), '=');
|
||||
|
||||
if (typeof Buffer !== 'undefined') {
|
||||
// Node.js environment
|
||||
return Buffer.from(base64String, 'base64').toString('utf8');
|
||||
} else if (typeof atob !== 'undefined') {
|
||||
// Browser environment
|
||||
const binaryString = atob(base64String);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return new TextDecoder().decode(bytes);
|
||||
} else {
|
||||
// Fallback pure JS implementation
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
let bytes: number[] = [];
|
||||
let i = 0;
|
||||
|
||||
while (i < base64String.length) {
|
||||
const encoded1 = chars.indexOf(base64String.charAt(i++));
|
||||
const encoded2 = chars.indexOf(base64String.charAt(i++));
|
||||
const encoded3 = chars.indexOf(base64String.charAt(i++));
|
||||
const encoded4 = chars.indexOf(base64String.charAt(i++));
|
||||
|
||||
const bitmap = (encoded1 << 18) | (encoded2 << 12) | (encoded3 << 6) | encoded4;
|
||||
|
||||
bytes.push((bitmap >> 16) & 255);
|
||||
if (encoded3 !== 64) bytes.push((bitmap >> 8) & 255);
|
||||
if (encoded4 !== 64) bytes.push(bitmap & 255);
|
||||
}
|
||||
|
||||
return new TextDecoder().decode(new Uint8Array(bytes));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* handle base64 strings
|
||||
*/
|
||||
@@ -50,21 +129,24 @@ export let base64 = {
|
||||
* encodes the string
|
||||
*/
|
||||
encode: (stringArg: string) => {
|
||||
return plugins.jsBase64.encode(stringArg);
|
||||
return universalBase64.encode(stringArg);
|
||||
},
|
||||
|
||||
/**
|
||||
* encodes a stringArg to base64 uri style
|
||||
*/
|
||||
encodeUri: (stringArg: string) => {
|
||||
return plugins.jsBase64.encodeURI(stringArg);
|
||||
return universalBase64.encode(stringArg)
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=/g, '');
|
||||
},
|
||||
|
||||
/**
|
||||
* decodes a base64 encoded string
|
||||
*/
|
||||
decode: (stringArg: string) => {
|
||||
return plugins.jsBase64.decode(stringArg);
|
||||
return universalBase64.decode(stringArg);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@@ -1,5 +1,63 @@
|
||||
import * as plugins from './smartstring.plugins.js';
|
||||
|
||||
/**
|
||||
* Cross-platform random number generator
|
||||
* Uses crypto.getRandomValues in browser and Math.random as fallback
|
||||
*/
|
||||
const getRandomInt = (min: number, max: number): number => {
|
||||
if (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.getRandomValues) {
|
||||
// Browser environment with crypto API
|
||||
const range = max - min;
|
||||
const array = new Uint32Array(1);
|
||||
globalThis.crypto.getRandomValues(array);
|
||||
return min + (array[0] % range);
|
||||
} else {
|
||||
// Fallback to Math.random for environments without crypto
|
||||
return Math.floor(Math.random() * (max - min)) + min;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom implementation of randomatic pattern-based string generator
|
||||
* Pattern characters:
|
||||
* A - Uppercase letter
|
||||
* a - Lowercase letter
|
||||
* 0 - Number (0-9)
|
||||
* ! - Special character
|
||||
* * - Any character (A, a, 0, or !)
|
||||
*/
|
||||
const customRandomatic = (pattern: string, length?: number, options?: any): string => {
|
||||
const charSets = {
|
||||
'A': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
'a': 'abcdefghijklmnopqrstuvwxyz',
|
||||
'0': '0123456789',
|
||||
'!': '!@#$%^&*()_+-=[]{}|;:,.<>?',
|
||||
'*': 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?'
|
||||
};
|
||||
|
||||
// If length is provided, repeat the pattern to match length
|
||||
let actualPattern = pattern;
|
||||
if (length && length > pattern.length) {
|
||||
actualPattern = pattern.repeat(Math.ceil(length / pattern.length)).slice(0, length);
|
||||
} else if (length) {
|
||||
actualPattern = pattern.slice(0, length);
|
||||
}
|
||||
|
||||
let result = '';
|
||||
for (const char of actualPattern) {
|
||||
if (charSets[char]) {
|
||||
const charSet = charSets[char];
|
||||
const randomIndex = getRandomInt(0, charSet.length);
|
||||
result += charSet[randomIndex];
|
||||
} else {
|
||||
// If not a pattern character, use it literally
|
||||
result += char;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* creates a random string
|
||||
*
|
||||
@@ -17,7 +75,7 @@ export const createRandomString = (
|
||||
lengthArg?: number,
|
||||
optionsArg?: any
|
||||
): string => {
|
||||
return plugins.randomatic(patternArg, lengthArg, optionsArg);
|
||||
return customRandomatic(patternArg, lengthArg, optionsArg);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import * as plugins from './smartstring.plugins.js';
|
||||
|
||||
export class Domain {
|
||||
public fullName: string;
|
||||
public level1: string;
|
||||
@@ -14,14 +12,14 @@ export class Domain {
|
||||
public domainName;
|
||||
public subDomain;
|
||||
public port;
|
||||
public nodeParsedUrl: plugins.url.UrlWithStringQuery;
|
||||
public nodeParsedUrl: URL;
|
||||
constructor(domainStringArg: string) {
|
||||
// lets do the node standard stuff first
|
||||
this.protocol = this._protocolRegex(domainStringArg);
|
||||
if (!this.protocol) {
|
||||
domainStringArg = `https://${domainStringArg}`;
|
||||
}
|
||||
this.nodeParsedUrl = plugins.url.parse(domainStringArg);
|
||||
this.nodeParsedUrl = new URL(domainStringArg);
|
||||
this.port = this.nodeParsedUrl.port;
|
||||
|
||||
// lets do the rest after
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import * as plugins from './smartstring.plugins.js';
|
||||
|
||||
/**
|
||||
* replaces all occurences of something in a string
|
||||
* @param stringArg
|
||||
@@ -10,6 +8,38 @@ export const replaceAll = (stringArg: string, searchPattern: string, replacement
|
||||
return stringArg.replace(new RegExp(searchPattern, 'g'), replacementString);
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom implementation of strip-indent
|
||||
* Removes the minimum indentation from all lines
|
||||
*/
|
||||
const stripIndent = (str: string): string => {
|
||||
const lines = str.split('\n');
|
||||
|
||||
// Find the minimum indentation (ignoring empty lines)
|
||||
let minIndent = Infinity;
|
||||
for (const line of lines) {
|
||||
if (line.trim().length > 0) {
|
||||
const match = line.match(/^(\s*)/);
|
||||
if (match) {
|
||||
minIndent = Math.min(minIndent, match[1].length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no indentation found, return original string
|
||||
if (minIndent === Infinity || minIndent === 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
// Remove the minimum indentation from all lines
|
||||
return lines.map(line => {
|
||||
if (line.length >= minIndent) {
|
||||
return line.slice(minIndent);
|
||||
}
|
||||
return line;
|
||||
}).join('\n');
|
||||
};
|
||||
|
||||
export interface INormalizeOptions {
|
||||
stripLeadingTrailingEmptyLines?: boolean;
|
||||
stripAllEmptyLines?: boolean;
|
||||
@@ -27,7 +57,7 @@ export const standard = (stringArg: string, options?: INormalizeOptions): string
|
||||
let result = stringArg;
|
||||
|
||||
if (!options || options.stripIndent) {
|
||||
result = plugins.stripIndent(result); // fix indention
|
||||
result = stripIndent(result); // fix indention
|
||||
}
|
||||
|
||||
if (!options || options.normalizeNewline) {
|
||||
|
@@ -1,17 +1,4 @@
|
||||
// node native
|
||||
import * as smartenv from '@push.rocks/smartenv';
|
||||
const smartenvInstance = new smartenv.Smartenv();
|
||||
// @push.rocks ecosystem
|
||||
import * as isounique from '@push.rocks/isounique';
|
||||
|
||||
export { isounique };
|
||||
|
||||
import * as url from 'url';
|
||||
export { url };
|
||||
|
||||
// third party
|
||||
import { Base64 as jsBase64 } from 'js-base64';
|
||||
|
||||
import stripIndent from 'strip-indent';
|
||||
import randomatic from 'randomatic';
|
||||
|
||||
export { jsBase64, stripIndent, randomatic };
|
||||
|
Reference in New Issue
Block a user