Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ These changes improve throughput and reduce memory pressure when working with la
- [stringPermutations](#stringpermutations) - Generates all unique permutations of a given string.
- [stringPermutationsGenerator](#stringpermutationsgenerator) - Generator-based permutations API for lazy iteration.
- [stringCombinations](#stringcombinations) - Generates all unique combinations of a given string.
- [pipeLine](#pipeline) - Can be used to chain multiple transformations, and powerful tools to manipulate data flow

### Validations

Expand Down Expand Up @@ -473,6 +474,146 @@ deburr('über cool');
| --------- | ------ | -------- | ----------------------------------------- |
| text | string | required | The input string to strip diacritics from |

#### <a id="pipeLine"></a>`pipeLine(args)`
Apply multiple transformations in any order and add effects that detect
changes during transformation process.

```typescript
pipeLine({
initial: "Hello World!",
pipes: [ camelCase, capitalize ]
}).output; // Helloworld
```
Optionally, an effect callback can be added to have more effects in-between transformations.

```typescript
pipeLine({
initial: "Evil Code Exceeding So Much Space!",
pipe: [camelCase, removeDuplicates],
effect({ target, restartBeforeTransform }) {

if (target.length > 10) {
//evil code
restartBeforeTransform(target.slice(0, -1))
}

}
}).output // evilcodeex
```

#### `pipeLine` Parameters

| Parameter | Type | Default | Description |
|------------|----------------------------------------|-----------|-----------------------------------------------------------------------------|
| `initial` | `string` | required | The initial input string to be processed. |
| `pipe` | `Array<(data: string) => string>` | required | Transformation functions applied sequentially to the input string. |
| `effect` | `(args: EffectArgs) => any` | optional | Optional effect function with access to transformation context and control. |

#### `effect` Function Context (`EffectArgs`)

| Property | Type | Description |
|------------------------|---------------------------------------------------|-----------------------------------------------------------------------------|
| `history` | `Array<{ operation: string, value: string }>` | A log of each transformation step and its result. |
| `first` | `string` | The original string before any transformations. |
| `target` | `string` | The final string after all transformations are complete. |
| `pipe` | `Array<(data: string) => string>` | The original array of transformation functions. |
| `abort` | `() => any` | Stops pipeline execution immediately. |
| `forceStop` | `(last: string) => any` | Halts processing and returns the current result. |
| `next` | `(string: string) => any` | Continues pipeline execution from the next transformation. |
| `restartBeforeTransform` | `(string?: string) => any` | Restarts the pipeline before calling the effect; optionally with new input.|
| `restartAfterTransform` | `(string?: string) => any` | Restarts the pipeline after calling the effect; optionally with new input. |
| `moveToPipeIndex` | `(index: number) => any` | Jumps to a specific transformation index to resume processing. |

#### <a id="pipe"></a>`pipe(initial)`

Lightweight and framework-agnostic string transformer with optional effect hooks and full pipeline control. Chain `.flow()` to get output or `.raw()` for transformation metadata.

```ts
pipe("Evil Code Exceeding So Much Space!")
.effect(({ target, restartBeforeTransform }) => {

if (target.length > 10) {
restartBeforeTransform(target.slice(0, -1))
}

})
.flow(camelCase, removeDuplicates) // evilcodeex
```

| Parameter | Type | Default | Description |
|-----------|--------|----------|--------------------------------------|
| initial | string | required | The input string to be transformed. |

---

#### <a id="pipe.flow"></a>`pipe().flow(...pipe)`

Applies all transformation functions in order and returns the final output string.

```ts
pipe("quick brown fox").flow(trim, camelCase); // quickBrownFox
```

| Parameter | Type | Default | Description |
|-----------|----------------------------------------|----------|------------------------------------------------|
| pipe | `((data: string) => string)[]` | required | List of transformation functions to apply. |

---

#### <a id="pipe.effect"></a>`pipe().effect(callback)`

Registers an effect hook that runs after transformation with full context and pipeline controls.

```ts
pipe("too long string!!")
.effect(({ target, restartBeforeTransform }) => {
if (target.length > 10) {
restartBeforeTransform(target.slice(0, 10));
}
})
.flow(removeSymbols); // toolongstr
```

| Parameter | Type | Default | Description |
|-----------|----------------|----------|----------------------------------------------|
| callback | `EffectArgs` | required | Hook with full access to pipeline control. |

---

#### <a id="pipe.raw"></a>`pipe().raw(...pipe)`

Same as `.flow()`, but returns a full result object including the transformation history and utilities.

```ts
const result = pipe("hello evil evil code")
.effect(({ history }) => console.log(history))
.raw(removeDuplicates);

console.log(result.output); // helloEvilCode
```

| Parameter | Type | Default | Description |
|-----------|----------------------------------------|----------|----------------------------------------------|
| pipe | `((data: string) => string)[]` | required | List of transformation functions to apply. |

---

#### <a id="EffectArgs"></a>`EffectArgs` (effect context)

| Property | Type | Description |
|--------------------------|-------------------------------------------------|--------------------------------------------------------------------------|
| `history` | `Array<{ operation: string, value: string }>` | Log of each transformation step and its result. |
| `first` | `string` | Original string before any transformations. |
| `target` | `string` | Final result after transformations. |
| `pipe` | `((data: string) => string)[]` | The transformation pipeline. |
| `abort` | `() => void` | Stop pipeline execution immediately. |
| `forceStop` | `(last: string) => void` | Stop execution and return a specific value. |
| `next` | `(newString: string) => void` | Continue to the next step with a different string. |
| `restartBeforeTransform` | `(newInitial?: string) => void` | Restart pipeline from beginning before calling the effect. |
| `restartAfterTransform` | `(newInitial?: string) => void` | Restart pipeline after the effect has been called. |
| `moveToPipeIndex` | `(index: number) => void` | Jump to a specific transformation step and continue. |


---

#### <a id="splitchunks"></a>`splitChunks(text, chunkSize)`
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ export default {
formatting,
transformations,
validations,
};
};
138 changes: 138 additions & 0 deletions src/tests/transformations/pipeLine.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { describe, it } from 'node:test';
import assert, { match } from 'node:assert';
import { pipe, pipeLine } from "../../transformations/pipe";
import { capitalize } from '../../formatting';
import { camelCase, removeDuplicates, truncateText } from '../../transformations';

describe('pipeLine', () => {

it('can run piped function', () => {
assert.strictEqual(
pipeLine({
initial: "hello world world",
pipe: [capitalize, removeDuplicates, camelCase]
}).output, "helloWorld"
)
});

it('can abort', () => {

assert.strictEqual(
pipeLine({

initial: "Phone Number: 123456",

pipe: [
capitalize, removeDuplicates, camelCase /*should abort here*/, (text) => truncateText(text, 5)
],

effect({ abort, target }) {
if (target == "phoneNumber123456") abort()
}
}

).output, "phoneNumber123456"

);
})

it('can restart', () => {
assert.strictEqual(
pipeLine({

initial: "123456",

pipe: [
(text) => text.slice(0, -1)
],

effect({ restartAfterTransform, target }) {
if (target != "") restartAfterTransform()
}

}).output, ""
)
});

it('can force stop', () => {

assert.strictEqual(
pipeLine({

initial: "I ate the the apple",

pipe: [
removeDuplicates, (text) => text
],

//called after every transformation
effect({ history, forceStop, target }) {
if (target.includes('apple') && history.length > 1) {
forceStop(target.replace('apple', '*****'))
}
}

}).output
, "I ate the *****"
)

});

it('testing restart before', () => {

assert.strictEqual(

pipeLine({
initial: "Evil Code Exceeding So Much Space!",
pipe: [camelCase, removeDuplicates],
effect({ target, restartBeforeTransform }) {

if (target.length > 10) {
//evil code
restartBeforeTransform(target.slice(0, -1))
}

}
}).output, "evilcodeex"
)

});

it('simple pipe test', () => {

assert.strictEqual(
pipe("Hello World").flow(camelCase), "helloWorld"
)

});

it('testing restart before using simple pipes', () => {

assert.strictEqual(
pipe("Evil Code Exceeding So Much Space!")
.effect(({ target, restartBeforeTransform }) => {

if (target.length > 10) {
restartBeforeTransform(target.slice(0, -1))
}

})
.flow(camelCase, removeDuplicates),

"evilcodeex"
)

});

it('reuseable pipes', () => {

const profanityFilter = (input: string) => pipe(input).flow(removeDuplicates, camelCase, (text) => text.replace('apple', '*****').replace("Apple", '*****'))

assert.strictEqual(
//seems like there's an issue with question mark?
profanityFilter("Who the apple are you?"), "whoThe*****AreYou "
)

})

})
5 changes: 4 additions & 1 deletion src/transformations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { numberToText } from './numberToText/main';
import { reverseWordsInString } from './reverseWordsInString ';
import { stringPermutations, stringPermutationsGenerator } from './stringPermutations';
import { stringCombinations } from './stringCombinations';
import { pipe, pipeLine } from './pipe';

export const transformations = {
camelCase,
Expand All @@ -60,5 +61,7 @@ export const transformations = {
reverseWordsInString,
stringPermutations,
stringPermutationsGenerator,
stringCombinations
stringCombinations,
pipeLine,
pipe
};
Loading