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:
96
changelog.md
Normal file
96
changelog.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-09-12 - 4.1.0 - feat(core)
|
||||
Introduce native implementations for Base64, random generation and normalization; remove runtime plugin dependencies; update tests, docs and package metadata
|
||||
|
||||
- Implemented a cross-platform universal Base64 encoder/decoder and replaced usage of external js-base64 with the internal implementation
|
||||
- Added a custom, cross-platform random string generator (uses crypto.getRandomValues when available) and removed dependency on randomatic / crypto-random-string
|
||||
- Replaced strip-indent usage with an internal stripIndent implementation in normalize utilities
|
||||
- Switched domain parsing to use the standard URL class instead of external url.parse
|
||||
- Simplified ts/smartstring.plugins.ts to only export @push.rocks/isounique and removed several plugin re-exports (js-base64, strip-indent, randomatic, url, smartenv)
|
||||
- Updated test imports to use @git.zone/tstest/tapbundle
|
||||
- Expanded and updated README with full usage, examples and API reference
|
||||
- Updated package.json: trimmed dependencies, bumped @git.zone/tstest version, added packageManager (pnpm) entry and adjusted files/browserslist
|
||||
- Added .claude/settings.local.json
|
||||
|
||||
## 2024-05-29 - 4.0.15 - maintenance
|
||||
Package metadata and TypeScript configuration updates.
|
||||
|
||||
- Updated package description.
|
||||
- TypeScript configuration (tsconfig) adjustments.
|
||||
- npmextra.json updated (githost entries).
|
||||
|
||||
## 2024-03-03 - 4.0.14 → 4.0.1 - maintenance & core fixes
|
||||
Routine core fixes, small updates and version bumps across the 4.0.x series.
|
||||
|
||||
- Multiple "fix(core): update" commits addressing internal/core issues.
|
||||
- Several version-only releases (patch bumps and releases with minimal change notes).
|
||||
|
||||
## 2022-03-18 - 4.0.2 → 3.0.26 - release consolidation
|
||||
Consolidated releases and small fixes spanning late 2020–early 2022.
|
||||
|
||||
- Several patch releases with minor fixes and version bumps.
|
||||
- Routine maintenance and stability improvements.
|
||||
|
||||
## 2018-11-28 - 3.0.4 → 3.0.0 - CI & dependency fixes
|
||||
Improvements to build/CI and dependency cleanups for the 3.0.x line.
|
||||
|
||||
- fix(dependencies and structure): updated dependencies and project structure (3.0.4).
|
||||
- fix(ci): reduced build dependencies and fixed build steps (3.0.3, 3.0.2).
|
||||
- fix(dependencies): updated test framework and removed obsolete dependency on typings-global (3.0.1, 3.0.0).
|
||||
- Numerous small fixes and version bump releases across 3.0.x.
|
||||
|
||||
## 2018-07-21 - 2.0.28 - BREAKING CHANGE: package scope
|
||||
Breaking change: package scope changed.
|
||||
|
||||
- Changed package scope to @pushrocks (BREAKING CHANGE).
|
||||
|
||||
## 2017-10-26 - 2.0.27 → 2.0.25 - code quality & npm metadata
|
||||
Small refactors and npm metadata additions.
|
||||
|
||||
- Refactor to use const (2.0.27).
|
||||
- Added npmextra.json (2.0.26).
|
||||
- Added create module (2.0.25).
|
||||
|
||||
## 2016-10-31 - 2.0.19 → 2.0.17 - Base64 & exports
|
||||
Added Base64 handling and improved exports.
|
||||
|
||||
- Added Base64 handling and exposed base64 functions separate from class (2.0.17–2.0.19).
|
||||
- Small export fixes.
|
||||
|
||||
## 2016-07-17 - 2.0.14 → 2.0.12 - ES6, indent/deindent, tests
|
||||
Feature enhancements around string indentation and ES6 migration.
|
||||
|
||||
- Switched codebase to ES6 (2.0.14).
|
||||
- Implemented deindent and working indent module; added tests (2.0.12).
|
||||
- Prep work for indent functionality and recompiled builds (2.0.11, 2.0.10).
|
||||
|
||||
## 2016-06-21 - 2.0.9 → 2.0.6 - domain handling improvements
|
||||
Domain parsing and regex improvements.
|
||||
|
||||
- Fixed Domain regex to include '-' and '_' and improved fullName handling (2.0.9, 2.0.8).
|
||||
- Now evaluates Domains without protocol specified (2.0.6).
|
||||
|
||||
## 2016-05-25 - 2.0.4 → 2.0.0 - core features & CI/docs
|
||||
Major 2.0.0 work and related improvements.
|
||||
|
||||
- Now computes zoneName and detects protocol (2.0.0).
|
||||
- Added authors note, improved README and CI (AppVeyor, GitLab CI) across several commits.
|
||||
- Multiple small fixes, dependency updates and YML/CI tweaks (2.0.4, 2.0.3, 2.0.2, 2.0.1).
|
||||
|
||||
## 2016-05-16 - 1.0.3 → 1.0.1 - parser & typings fixes
|
||||
Parser improvements and TypeScript declaration fixes.
|
||||
|
||||
- Correctly parsing a Domain and structure updates (1.0.3).
|
||||
- Fixes for typings and declaration issues (1.0.2, 1.0.1).
|
||||
|
||||
## 2016-04-15 - 1.0.0 → 0.0.3 - initial stable release
|
||||
Initial stable release artifacts.
|
||||
|
||||
- Added TypeScript regex section and performed first 1.0.0 release (1.0.0).
|
||||
- Prior 0.0.x preparatory releases.
|
||||
|
||||
## 2016-02-23 - 0.0.0 → initial - project initialization
|
||||
Project initialization and first commits.
|
||||
|
||||
- Initial commit and early CI/travis updates.
|
15
package.json
15
package.json
@@ -38,19 +38,11 @@
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^2.1.72",
|
||||
"@git.zone/tsrun": "^1.2.42",
|
||||
"@git.zone/tstest": "^1.0.86",
|
||||
"@push.rocks/tapbundle": "^5.0.15",
|
||||
"@git.zone/tstest": "^2.3.7",
|
||||
"@types/node": "^20.11.24"
|
||||
},
|
||||
"dependencies": {
|
||||
"@push.rocks/isounique": "^1.0.5",
|
||||
"@push.rocks/smartenv": "^5.0.12",
|
||||
"@types/randomatic": "^3.1.5",
|
||||
"crypto-random-string": "^5.0.0",
|
||||
"js-base64": "^3.7.7",
|
||||
"randomatic": "^3.1.1",
|
||||
"strip-indent": "^4.0.0",
|
||||
"url": "^0.11.3"
|
||||
"@push.rocks/isounique": "^1.0.5"
|
||||
},
|
||||
"files": [
|
||||
"ts/**/*",
|
||||
@@ -66,5 +58,6 @@
|
||||
],
|
||||
"browserslist": [
|
||||
"last 1 chrome versions"
|
||||
]
|
||||
],
|
||||
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748"
|
||||
}
|
11676
pnpm-lock.yaml
generated
11676
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
358
readme.md
358
readme.md
@@ -1,135 +1,363 @@
|
||||
# @push.rocks/smartstring
|
||||
handle strings in smart ways. TypeScript ready.
|
||||
|
||||
🎯 **Smart string manipulation for TypeScript** - Your comprehensive toolkit for handling strings, domains, Git URLs, and encodings with elegance and precision.
|
||||
|
||||
## Why smartstring?
|
||||
|
||||
When working with strings in modern JavaScript/TypeScript applications, you often need more than just basic manipulation. You need to:
|
||||
- Parse and validate domains and URLs
|
||||
- Handle Git repository URLs across different formats
|
||||
- Encode/decode Base64 for data transmission
|
||||
- Normalize indentation in code generators
|
||||
- Create cryptographically secure random strings
|
||||
- Validate string encodings
|
||||
- Parse Docker environment variables
|
||||
|
||||
**smartstring** unifies all these capabilities in a single, tree-shakeable, TypeScript-native package with zero setup overhead.
|
||||
|
||||
## Install
|
||||
To install `@push.rocks/smartstring`, use the following npm command:
|
||||
|
||||
```bash
|
||||
npm install @push.rocks/smartstring --save
|
||||
```
|
||||
|
||||
This will add it to your project's dependencies.
|
||||
Or using pnpm (recommended):
|
||||
|
||||
```bash
|
||||
pnpm add @push.rocks/smartstring
|
||||
```
|
||||
|
||||
## Features at a Glance
|
||||
|
||||
✅ **Domain parsing** - Extract protocols, subdomains, domains, and TLDs
|
||||
✅ **Git URL handling** - Parse and convert between SSH and HTTPS formats
|
||||
✅ **Base64 encoding** - Standard and URL-safe Base64 operations
|
||||
✅ **Smart indentation** - Indent, outdent, and normalize multi-line strings
|
||||
✅ **Random strings** - Pattern-based and cryptographically secure generation
|
||||
✅ **String normalization** - Clean and standardize whitespace
|
||||
✅ **Type checking** - Validate UTF-8 and Base64 encodings
|
||||
✅ **Docker env parsing** - Transform Docker environment arrays to objects
|
||||
|
||||
## Usage
|
||||
|
||||
The `@push.rocks/smartstring` package provides a powerful set of utilities to handle and manipulate strings in various ways, ready for TypeScript usage. Here's an exhaustive guide to using this package.
|
||||
### 🌐 Domain Parsing
|
||||
|
||||
### Working with Domain Strings
|
||||
|
||||
The `Domain` class helps in parsing and extracting information from domain URLs.
|
||||
Extract detailed information from any URL or domain string:
|
||||
|
||||
```typescript
|
||||
import { Domain } from '@push.rocks/smartstring';
|
||||
|
||||
// Parse a domain URL
|
||||
const myDomain = new Domain('https://sub.example.com');
|
||||
console.log(myDomain.level1); // Output: "com"
|
||||
console.log(myDomain.level2); // Output: "example"
|
||||
console.log(myDomain.zoneName); // Output: "example.com"
|
||||
console.log(myDomain.protocol); // Output: "https"
|
||||
const domain = new Domain('https://subdomain.example.com:3000/path');
|
||||
|
||||
console.log(domain.protocol); // 'https'
|
||||
console.log(domain.hostname); // 'subdomain.example.com'
|
||||
console.log(domain.port); // '3000'
|
||||
console.log(domain.pathname); // '/path'
|
||||
console.log(domain.fullUrl); // 'https://subdomain.example.com:3000/path'
|
||||
|
||||
// Domain level extraction
|
||||
console.log(domain.level1); // 'com' - TLD
|
||||
console.log(domain.level2); // 'example' - Domain
|
||||
console.log(domain.level3); // 'subdomain' - Subdomain
|
||||
console.log(domain.zoneName); // 'example.com' - Full domain without subdomain
|
||||
console.log(domain.subdomain); // 'subdomain'
|
||||
```
|
||||
|
||||
### Handling Git Repositories
|
||||
### 🔧 Git Repository URLs
|
||||
|
||||
The `GitRepo` class is designed for extracting information from Git repository URLs.
|
||||
Parse and convert Git repository URLs between SSH and HTTPS formats:
|
||||
|
||||
```typescript
|
||||
import { GitRepo } from '@push.rocks/smartstring';
|
||||
|
||||
// Parse a Git repository URL
|
||||
const myGitRepo = new GitRepo('git@github.com:user/repo.git');
|
||||
console.log(myGitRepo.host); // Output: "github.com"
|
||||
console.log(myGitRepo.user); // Output: "user"
|
||||
console.log(myGitRepo.repo); // Output: "repo"
|
||||
console.log(myGitRepo.sshUrl); // Output: "git@github.com:user/repo.git"
|
||||
console.log(myGitRepo.httpsUrl); // Output: "https://github.com/user/repo.git"
|
||||
// Parse SSH format
|
||||
const repo = new GitRepo('git@github.com:user/awesome-project.git');
|
||||
console.log(repo.host); // 'github.com'
|
||||
console.log(repo.user); // 'user'
|
||||
console.log(repo.repo); // 'awesome-project'
|
||||
console.log(repo.httpsUrl); // 'https://github.com/user/awesome-project.git'
|
||||
|
||||
// Parse HTTPS format
|
||||
const httpsRepo = new GitRepo('https://gitlab.com/team/project.git');
|
||||
console.log(httpsRepo.sshUrl); // 'git@gitlab.com:team/project.git'
|
||||
console.log(httpsRepo.httpsUrl); // 'https://gitlab.com/team/project.git'
|
||||
```
|
||||
|
||||
### Encoding and Decoding Base64 Strings
|
||||
### 🔐 Base64 Encoding
|
||||
|
||||
`@push.rocks/smartstring` offers base64 encoding and decoding through the `Base64` class and utility functions.
|
||||
Handle Base64 encoding with both standard and URL-safe variants:
|
||||
|
||||
```typescript
|
||||
import { Base64, base64 } from '@push.rocks/smartstring';
|
||||
|
||||
// Using the Base64 class
|
||||
const myBase64 = new Base64('hello world', 'string');
|
||||
console.log(myBase64.base64String); // Encoded string
|
||||
console.log(myBase64.base64UriString); // Encoded URI compatible string
|
||||
const encoded = new Base64('Hello World! 👋', 'string');
|
||||
console.log(encoded.base64String); // Standard Base64
|
||||
console.log(encoded.base64UriString); // URL-safe Base64
|
||||
console.log(encoded.simpleString); // Decoded string
|
||||
|
||||
// Using utility functions
|
||||
const encoded = base64.encode('hello world');
|
||||
const decoded = base64.decode(encoded);
|
||||
console.log(encoded); // Encoded string
|
||||
console.log(decoded); // "hello world"
|
||||
const quickEncode = base64.encode('Secret message');
|
||||
const quickDecode = base64.decode(quickEncode);
|
||||
|
||||
// Validate Base64 strings
|
||||
console.log(base64.isBase64('SGVsbG8=')); // true
|
||||
console.log(base64.isBase64('Not Base64!')); // false
|
||||
|
||||
// URL-safe Base64 operations
|
||||
const urlSafeEncoded = base64.encodeUri('https://example.com/path?param=value');
|
||||
const urlSafeDecoded = base64.decodeUri(urlSafeEncoded);
|
||||
```
|
||||
|
||||
### Applying Indentation
|
||||
### 📐 Smart Indentation
|
||||
|
||||
SmartString allows you to easily indent strings or normalize indentation across a multi-line string.
|
||||
Manage indentation in multi-line strings with precision:
|
||||
|
||||
```typescript
|
||||
import { indent } from '@push.rocks/smartstring';
|
||||
|
||||
// Indent a string by 4 spaces
|
||||
const indentedString = indent.indent('Some text\nAnother line', 4);
|
||||
console.log(indentedString);
|
||||
// Indent with spaces
|
||||
const indented = indent.indent('Line 1\nLine 2\nLine 3', 4);
|
||||
// Result:
|
||||
// Line 1
|
||||
// Line 2
|
||||
// Line 3
|
||||
|
||||
// Indent using a prefix
|
||||
const prefixedString = indent.indentWithPrefix('Line 1\nLine 2', '> ');
|
||||
console.log(prefixedString);
|
||||
// Indent with custom prefix
|
||||
const prefixed = indent.indentWithPrefix('Item 1\nItem 2', '> ');
|
||||
// Result:
|
||||
// > Item 1
|
||||
// > Item 2
|
||||
|
||||
// Normalize indentation
|
||||
const normalizedString = indent.normalize(' Some indented text\n Another line');
|
||||
console.log(normalizedString);
|
||||
// Add prefix to first line only
|
||||
const firstLinePrefixed = indent.addPrefix('Chapter\nContent here', '# ');
|
||||
// Result:
|
||||
// # Chapter
|
||||
// Content here
|
||||
|
||||
// Normalize irregular indentation
|
||||
const messy = `
|
||||
function hello() {
|
||||
console.log('Hi');
|
||||
return true;
|
||||
}
|
||||
`;
|
||||
const clean = indent.normalize(messy);
|
||||
// Result: Properly aligned with minimum indentation preserved
|
||||
```
|
||||
|
||||
### Creating Random or Encrypted Strings
|
||||
### 🎲 Random String Generation
|
||||
|
||||
Create random strings based on patterns or generate cryptographically strong random strings.
|
||||
Create random strings with specific patterns or cryptographic security:
|
||||
|
||||
```typescript
|
||||
import { create } from '@push.rocks/smartstring';
|
||||
|
||||
// Create a random string
|
||||
const randomString = create.createRandomString('aA0', 10);
|
||||
console.log(randomString); // Example output: "a9mB8v2Dq1"
|
||||
// Pattern-based random strings
|
||||
// A = uppercase, a = lowercase, 0 = number, ! = special, * = any
|
||||
const password = create.createRandomString('Aa0!', 16);
|
||||
// Example: "Kg7$Lp2@Qm9#Xn4!"
|
||||
|
||||
// Create a crypto-random string
|
||||
const cryptoString = create.createCryptoRandomString(10);
|
||||
console.log(cryptoString); // Example output: "f28Bb90aCc"
|
||||
const alphanumeric = create.createRandomString('Aa0', 10);
|
||||
// Example: "K7gLp2Qm9X"
|
||||
|
||||
const numbers = create.createRandomString('0', 6);
|
||||
// Example: "472819"
|
||||
|
||||
// Cryptographically secure random strings
|
||||
const cryptoId = create.createCryptoRandomString();
|
||||
// Example: "f7b2d8e0-3c4a-4b9c-8d2e-1f0a7b9c8d7e"
|
||||
```
|
||||
|
||||
### Normalizing Strings
|
||||
### 🧹 String Normalization
|
||||
|
||||
Normalize strings by removing leading/trailing whitespace, fixing indentation, and more.
|
||||
Clean and standardize strings for consistent formatting:
|
||||
|
||||
```typescript
|
||||
import { normalize } from '@push.rocks/smartstring';
|
||||
|
||||
// Normalize a multi-line string
|
||||
const exampleString = `
|
||||
This is an example.
|
||||
The indentation will be fixed.
|
||||
const messyString = `
|
||||
This text has
|
||||
inconsistent indentation
|
||||
and too much whitespace
|
||||
|
||||
|
||||
between lines...
|
||||
`;
|
||||
const normalized = normalize.standard(exampleString);
|
||||
console.log(normalized);
|
||||
|
||||
const cleaned = normalize.standard(messyString);
|
||||
// Result: Properly formatted with consistent spacing
|
||||
|
||||
// Custom normalization
|
||||
const customNormalized = normalize.normal(messyString);
|
||||
```
|
||||
|
||||
### Working with Docker Environment Variables
|
||||
### 🔍 String Type Validation
|
||||
|
||||
Transform an array of Docker environment variables into an object for easy access.
|
||||
Check string encodings and types:
|
||||
|
||||
```typescript
|
||||
import { type } from '@push.rocks/smartstring';
|
||||
|
||||
// Check if string is valid UTF-8
|
||||
const isValidUtf8 = type.isUtf8('Hello 世界');
|
||||
console.log(isValidUtf8); // true
|
||||
|
||||
// Check if string is Base64
|
||||
const isBase64String = type.isBase64('SGVsbG8gV29ybGQ=');
|
||||
console.log(isBase64String); // true
|
||||
```
|
||||
|
||||
### 🐳 Docker Environment Variables
|
||||
|
||||
Parse Docker-style environment variable arrays:
|
||||
|
||||
```typescript
|
||||
import { docker } from '@push.rocks/smartstring';
|
||||
|
||||
const envVars = ['NODE_ENV=production', 'PORT=3000'];
|
||||
const envObject = docker.makeEnvObject(envVars);
|
||||
console.log(envObject.NODE_ENV); // Output: "production"
|
||||
console.log(envObject.PORT); // Output: "3000"
|
||||
const envArray = [
|
||||
'NODE_ENV=production',
|
||||
'PORT=3000',
|
||||
'DATABASE_URL=postgresql://localhost:5432/mydb',
|
||||
'API_KEY=secret123'
|
||||
];
|
||||
|
||||
const envObject = docker.makeEnvObject(envArray);
|
||||
|
||||
console.log(envObject.NODE_ENV); // 'production'
|
||||
console.log(envObject.PORT); // '3000'
|
||||
console.log(envObject.DATABASE_URL); // 'postgresql://localhost:5432/mydb'
|
||||
console.log(envObject.API_KEY); // 'secret123'
|
||||
|
||||
// Use in Docker-related configurations
|
||||
// Perfect for parsing process.env or Docker Compose outputs
|
||||
```
|
||||
|
||||
This guide covers the primary features of `@push.rocks/smartstring`, making string manipulation and information extraction simple and efficient in your TypeScript projects.
|
||||
## API Reference
|
||||
|
||||
### Classes
|
||||
|
||||
- **`Domain`** - URL/domain parser with component extraction
|
||||
- **`GitRepo`** - Git repository URL parser and converter
|
||||
- **`Base64`** - Base64 encoder/decoder with multiple formats
|
||||
|
||||
### Modules
|
||||
|
||||
- **`create`** - Random string generation
|
||||
- `createRandomString(pattern, length, options)` - Pattern-based generation
|
||||
- `createCryptoRandomString()` - Cryptographically secure strings
|
||||
|
||||
- **`indent`** - Indentation management
|
||||
- `indent(text, spaces)` - Add spaces to each line
|
||||
- `indentWithPrefix(text, prefix)` - Add custom prefix to each line
|
||||
- `normalize(text)` - Fix inconsistent indentation
|
||||
- `addPrefix(text, prefix)` - Add prefix to first line only
|
||||
|
||||
- **`normalize`** - String normalization
|
||||
- `standard(text)` - Apply standard normalization
|
||||
- `normal(text)` - Basic normalization
|
||||
|
||||
- **`type`** - String type checking
|
||||
- `isUtf8(text)` - Validate UTF-8 encoding
|
||||
- `isBase64(text)` - Validate Base64 format
|
||||
|
||||
- **`base64`** - Base64 utilities
|
||||
- `encode(text)` - Standard Base64 encoding
|
||||
- `decode(text)` - Standard Base64 decoding
|
||||
- `encodeUri(text)` - URL-safe Base64 encoding
|
||||
- `decodeUri(text)` - URL-safe Base64 decoding
|
||||
- `isBase64(text)` - Check if string is valid Base64
|
||||
|
||||
- **`docker`** - Docker utilities
|
||||
- `makeEnvObject(envArray)` - Convert env array to object
|
||||
|
||||
## Real-World Examples
|
||||
|
||||
### Building a URL Shortener
|
||||
|
||||
```typescript
|
||||
import { Domain, create, base64 } from '@push.rocks/smartstring';
|
||||
|
||||
function createShortUrl(longUrl: string): string {
|
||||
const domain = new Domain(longUrl);
|
||||
const shortCode = create.createRandomString('Aa0', 6);
|
||||
const encoded = base64.encodeUri(`${domain.hostname}${domain.pathname}`);
|
||||
|
||||
return `short.ly/${shortCode}`;
|
||||
}
|
||||
```
|
||||
|
||||
### Processing Code Templates
|
||||
|
||||
```typescript
|
||||
import { indent, normalize } from '@push.rocks/smartstring';
|
||||
|
||||
function generateComponent(name: string, props: string[]): string {
|
||||
const propsSection = props
|
||||
.map(prop => `${prop}: string;`)
|
||||
.join('\n');
|
||||
|
||||
const template = `
|
||||
export interface ${name}Props {
|
||||
${indent.indent(propsSection, 2)}
|
||||
}
|
||||
|
||||
export function ${name}(props: ${name}Props) {
|
||||
${indent.indent('// Component implementation', 2)}
|
||||
}
|
||||
`;
|
||||
|
||||
return normalize.standard(template);
|
||||
}
|
||||
```
|
||||
|
||||
### Git Repository Manager
|
||||
|
||||
```typescript
|
||||
import { GitRepo } from '@push.rocks/smartstring';
|
||||
|
||||
class RepoManager {
|
||||
repos: Map<string, GitRepo> = new Map();
|
||||
|
||||
addRepo(url: string): void {
|
||||
const repo = new GitRepo(url);
|
||||
this.repos.set(repo.repo, repo);
|
||||
}
|
||||
|
||||
getCloneCommand(name: string, useSSH = false): string {
|
||||
const repo = this.repos.get(name);
|
||||
if (!repo) throw new Error('Repository not found');
|
||||
|
||||
const url = useSSH ? repo.sshUrl : repo.httpsUrl;
|
||||
return `git clone ${url}`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Browser Support
|
||||
|
||||
This package is built for modern environments and includes:
|
||||
- ✅ Full ES Module support
|
||||
- ✅ Tree-shaking ready
|
||||
- ✅ TypeScript definitions
|
||||
- ✅ Browser-compatible (via bundlers)
|
||||
- ✅ Node.js 14+ support
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://code.foss.global/push.rocks/smartstring.git
|
||||
|
||||
# Install dependencies
|
||||
pnpm install
|
||||
|
||||
# Run tests
|
||||
pnpm test
|
||||
|
||||
# Build the project
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as smartstring from '../ts/index.js';
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
// Base64
|
||||
let testBase64: smartstring.Base64;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as smartstring from '../ts/index.js';
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
// Docker
|
||||
tap.test('expect create a Env Object', async () => {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as smartstring from '../ts/index.js';
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
// Domain
|
||||
let testDomain: smartstring.Domain;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as smartstring from '../ts/index.js';
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
// git
|
||||
let testGit: smartstring.GitRepo;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as smartstring from '../ts/index.js';
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
// indent
|
||||
let testString = `
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
import * as smartstring from '../ts/index.js';
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import * as smartstring from '../ts/index.js';
|
||||
|
||||
tap.test('should state valuid utf8', async () => {
|
||||
|
@@ -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