website hosted

This commit is contained in:
root
2025-10-17 21:06:57 +00:00
parent 14b2d53e8e
commit 73cdd255a9
308 changed files with 88840 additions and 9 deletions

View File

@@ -0,0 +1,45 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Checks
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
Lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: 18
- name: Install Dependencies
run: npm ci
- name: Check Lint
run: npm run lint
Test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14, 16, 18, 19]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: npm ci
- name: Run Tests
run: npm test

View File

@@ -0,0 +1,8 @@
{
"cSpell.words": [
"papaper"
],
"javascript.format.semicolons": "remove",
"javascript.format.insertSpaceAfterConstructor": true,
"javascript.format.insertSpaceBeforeFunctionParenthesis": true
}

21
node_modules/stream-replace-string/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 ChocolateLoverRaj
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

55
node_modules/stream-replace-string/README.md generated vendored Normal file
View File

@@ -0,0 +1,55 @@
# stream-replace-string
Replaces strings in a stream.
## Install
```shell
npm i stream-replace-string
```
## Using
```javascript
import replace from 'stream-replace-string'
import { createReadStream, createWriteStream } from 'fs'
createReadStream("./input.txt")
.pipe(replace("{fruit}", "apple"))
.pipe(createWriteStream("./output.txt"))
```
You can also replace multiple different words by piping your data through multiple replace streams
```javascript
createReadStream("./input.txt")
.pipe(replace("good", "great"))
.pipe(replace("bad", "horrible"))
.pipe(replace("grey", "gray"))
.pipe(createWriteStream("./output.txt"))
```
### The `replace` function
```javascript
replace(searchStr, replaceWith, options)
```
- Parameters
- `searchStr` - The string to search for. This must be a string, and cannot be a regex.
- `replaceWith` - There are a couple of different things you can use:
- String - Inserts the string.
- Promise resolving a string - Once the promise is resolved, the given string is used.
- Replacer function - This is a custom replacer function. The function can take a parameter, `matches`, which is the number of matches so far (for the first match, this will be `0`). This function should return a string or a promise resolving a string.
- Readable stream - A readable stream can be given, which can make more chunks available sooner. See `options.bufferReplaceStream` for options if you are using a readable stream.
- `options` (optional) - An object of options.
- `limit` (optional, default `Infinity`) - The maximum number of strings to replace. This can be useful if you know there is only 1 occurrence of `searchStr`. Once this limit is reached, the transform stream will stop transforming anything.
- `bufferReplaceStream` (optional, default `true`) - This is for when you use a readable stream for `replaceWith`. If this is true, the `replaceWith` stream will be read right away, and be kept in memory to be used once a match is found. For fastest performance, keep this to be `true`. If your `replaceWith` stream is very large and you have a limit of 1, you can set this to `false` to save memory.
- Returns
- Returns a transform stream. See the [How It Works](#how-it-works) section for more information.
## How It Works
### The transform stream
The [`replace`](#the-`replace`-function) function returns a Node.js transform stream. Transform streams take in chunks and can also be read. In this particular transform stream, it takes a string and outputs the same string, except that it replaces the replace.
### Efficient memory use.
The tricky part with this transform stream is knowing when to hold chunks, and when to pass them on. Let's say we were looking for `'paper'` in our text. If our first chunk was: `'perfect pot'`, this module knows that there is no way the string `'perfect pot'` will fit into the search string, `'paper'`. We can then pass the chunks onto the output of the transform stream, available right away for the stream consumer. However, if our first chunk was: `'p'`, we can't pass that on, because there is a change that the next chunk could start with `'aper'`. Since we aren't sure if the `'p'` will be replaced or not, we hold on to this text, and check it once we get the next chunk. If we get `'aper'` in the next chunk, we replace the text. If we get something else, like `'ear'`, we can attach it to the `'p'` and output `'pear'`. It gets even more complicated when multiple potential matches are possible. Let's say we get `'pap'` in our first chunk. We need to be watching the first `'p'` and the third `'p'`, because it could end up being `'paper'`, if the next chunk was `'er'`, or it could end of being `'papaper'`. This package is smart and it will efficiently find matches spanning multiple chunks, and get rid of text it knows won't have a match.
#### Importing with ESModules
```javascript
import replace from 'stream-replace-string'
```

13
node_modules/stream-replace-string/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,13 @@
import { Transform, Readable } from 'stream';
interface Options {
limit?: number;
bufferReplaceStream?: boolean;
}
type ReplaceStr = string | Promise<string>;
type ReplacerFunc = (matches?: number) => ReplaceStr;
declare function replace(searchStr: string, replaceWith: ReplaceStr | ReplacerFunc | Readable, options?: Options): Transform;
export default replace;

238
node_modules/stream-replace-string/index.js generated vendored Normal file
View File

@@ -0,0 +1,238 @@
import { Transform, Readable } from 'stream'
import { StringDecoder } from 'string_decoder'
/**
*
* @param {string} searchStr
* @param {string} replaceWith
* @param {number} limit
*/
const replace = (searchStr, replaceWith, options = {}) => {
// Defaulting
if (!Object.prototype.hasOwnProperty.call(options, 'limit')) {
options.limit = Infinity
}
if (!Object.prototype.hasOwnProperty.call(options, 'bufferReplaceStream')) {
options.bufferReplaceStream = true
}
// Type checking
if (typeof searchStr !== 'string') {
throw new TypeError('searchStr must be a string.')
}
if (!(
typeof replaceWith === 'string' ||
replaceWith instanceof Promise ||
typeof replaceWith === 'function' ||
replaceWith instanceof Readable
)) {
throw new TypeError('replaceWith must be either a string, a promise resolving a string, a function returning string, a function returning a promise resolving a string, or a readable stream.')
}
if (typeof options !== 'object') {
throw new TypeError('options must be an object.')
}
if (!(
(Number.isInteger(options.limit) && options.limit > 0) ||
(options.limit === Infinity)
)) {
throw new TypeError('options.limit must be a positive integer or infinity.')
}
if (typeof options.bufferReplaceStream !== 'boolean') {
throw new TypeError('options.bufferReplaceStream must be a boolean.')
}
const limit = options.limit
// This stuff is for if replaceWith is a readable stream
let replaceWithBuffer = ''
let replaceWithNewChunk
let isDecodingReplaceWithStream = false
const startDecodingReplaceWithStream = () => {
isDecodingReplaceWithStream = true
const stringDecoder = new StringDecoder('utf-8')
const dataHandler = data => {
replaceWithNewChunk = stringDecoder.write(data)
if (options.bufferReplaceStream) {
replaceWithBuffer += replaceWithNewChunk
}
}
const endHandler = () => {
replaceWithNewChunk = stringDecoder.end()
if (options.bufferReplaceStream) {
replaceWithBuffer += replaceWithNewChunk
}
}
replaceWith
.on('data', dataHandler)
.on('end', endHandler)
}
if (replaceWith instanceof Readable) {
if (options.bufferReplaceStream) {
startDecodingReplaceWithStream()
} else {
replaceWith.pause()
}
}
// Create a new string decoder to turn buffers into strings
const stringDecoder = new StringDecoder('utf-8')
// Whether the limit has been reached
let doneReplacing = false
// Then number of matches
let matches = 0
// The in progress matches waiting for next chunk to be continued
/**
* An array of numbers, each number is an index which marks the start of the string `unsureBuffer`
*/
let partialMatchesFromPrevChunk = []
// The string data that we aren't yet sure it's part of the search string or not
// We have to hold on to this until we are sure.
let unsureBuffer = ''
// Get the replace string
let replaceStr = typeof replaceWith === 'string' ? replaceWith : undefined
const pushReplaceStr = async () => {
switch (typeof replaceWith) {
case 'string': {
transform.push(replaceStr)
break
}
case 'function': {
const returnedStr = await replaceWith(matches)
if (typeof returnedStr !== 'string') {
throw new TypeError('Replace function did not return a string or a promise resolving a string.')
}
transform.push(returnedStr)
break
}
case 'object': {
if (replaceWith instanceof Promise) {
replaceStr = await replaceWith
if (typeof replaceStr !== 'string') {
throw new TypeError('Replace promise did not resolve to a string.')
}
transform.push(replaceStr)
} else if (replaceWith instanceof Readable) {
await new Promise((resolve, reject) => {
if (!isDecodingReplaceWithStream) {
startDecodingReplaceWithStream()
}
// Push the buffer so far
transform.push(replaceWithBuffer)
if (!replaceWith.readableEnded) {
replaceWith
.on('data', () => {
transform.push(replaceWithNewChunk)
})
.once('end', () => {
transform.push(replaceWithNewChunk)
resolve()
})
replaceWith.resume()
} else {
resolve()
}
})
}
break
}
default: {
throw new Error("This shouldn't happen.")
}
}
}
const foundMatch = async () => {
await pushReplaceStr()
matches++
if (matches === limit) {
doneReplacing = true
}
}
const transform = new Transform({
async transform (chunk, encoding, callback) {
if (doneReplacing) {
callback(undefined, chunk)
} else {
// Convert to utf-8
let chunkStr = stringDecoder.write(chunk)
/**
* This is the end index of the string that we might replace later
*/
let keepEndIndex
// Continue / complete / discard partial matches from previous chunks
for (let i = 0; i < partialMatchesFromPrevChunk.length;) {
const pastChunkMatchIndex = partialMatchesFromPrevChunk[i]
const chunkStrEndIndex = searchStr.length - (unsureBuffer.length - pastChunkMatchIndex)
const strToCheck = unsureBuffer.slice(pastChunkMatchIndex) + chunkStr.slice(0, chunkStrEndIndex)
if (searchStr.startsWith(strToCheck)) {
if (strToCheck.length >= searchStr.length) {
// Match completed
// Push the previous part of the string that was saved
transform.push(unsureBuffer.slice(0, pastChunkMatchIndex))
await foundMatch()
unsureBuffer = ''
partialMatchesFromPrevChunk = []
chunkStr = chunkStr.slice(chunkStrEndIndex)
} else {
// Match continued
keepEndIndex = keepEndIndex ?? 0
i++
}
} else {
// Match discarded
partialMatchesFromPrevChunk.splice(i, 1)
}
}
// Check for completed / partial matches in the current chunk
let chunkStrIndex = 0
while (chunkStrIndex < chunkStr.length) {
const strToCheck = chunkStr.slice(chunkStrIndex, chunkStrIndex + searchStr.length)
if (searchStr.startsWith(strToCheck)) {
if (strToCheck.length === searchStr.length) {
// Match completed
transform.push(chunkStr.slice(0, chunkStrIndex))
await foundMatch()
chunkStr = chunkStr.slice(chunkStrIndex + strToCheck.length)
chunkStrIndex = 0
} else {
// Partial match
partialMatchesFromPrevChunk.push(chunkStrIndex)
keepEndIndex = keepEndIndex ?? chunkStrIndex
chunkStrIndex++
}
} else {
// No match
if (keepEndIndex === undefined) {
// Push the string out right away
transform.push(chunkStr.slice(chunkStrIndex, chunkStrIndex + 1))
chunkStr = chunkStr.slice(chunkStrIndex + 1)
} else {
// We are keeping the string
chunkStrIndex++
}
}
}
// Save the part of the chunk that might be part of a match later
unsureBuffer += chunkStr.slice(keepEndIndex)
// This is needed
callback()
}
},
flush (callback) {
// Release the unsureBuffer
callback(undefined, unsureBuffer)
}
})
return transform
}
export default replace

35
node_modules/stream-replace-string/package.json generated vendored Normal file
View File

@@ -0,0 +1,35 @@
{
"name": "stream-replace-string",
"version": "2.0.0",
"description": "Replaces strings in a stream.",
"main": "./index.js",
"types": "./index.d.ts",
"type": "module",
"scripts": {
"lint": "standard",
"test": "node test.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ChocolateLoverRaj/stream-replace-string.git"
},
"keywords": [
"string",
"replace",
"find",
"find-and-replace",
"stream",
"transform"
],
"author": "Rajas Paranjpe",
"license": "MIT",
"bugs": {
"url": "https://github.com/ChocolateLoverRaj/stream-replace-string/issues"
},
"homepage": "https://github.com/ChocolateLoverRaj/stream-replace-string#readme",
"devDependencies": {
"baretest": "^2.0.0",
"standard": "^17.0.0",
"stream-to-string": "^1.2.0"
}
}

64
node_modules/stream-replace-string/test.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
import baretest from 'baretest'
import { strictEqual } from 'assert'
import replace from './index.js'
import { Readable } from 'stream'
import streamToString from 'stream-to-string'
const test = baretest('Tests')
test('no change', async () => {
strictEqual(await streamToString(Readable.from([
'paper\n',
'pajamas\n',
'socks\n'
]).pipe(replace('pinapple', 'apple'))), 'paper\npajamas\nsocks\n')
})
test('chunk boundary', async () => {
strictEqual(await streamToString(Readable.from([
'pap',
'aper',
'socks'
]).pipe(replace('paper', 'stuff that we write on'))), 'pastuff that we write onsocks')
})
test('within chunk boundary', async () => {
strictEqual(await streamToString(Readable.from([
'red\n',
'grey\n',
'orange\n'
]).pipe(replace('grey', 'gray'))), 'red\ngray\norange\n')
})
test('split over multiple chunks', async () => {
strictEqual(await streamToString(Readable.from([
'|a',
'pp',
'le|'
]).pipe(replace('apple', 'mela'))), '|mela|')
})
test('split over multiple chunks 2', async () => {
strictEqual(await streamToString(Readable.from([
'|apple a',
'pp',
'le|'
]).pipe(replace('apple', 'mela'))), '|mela mela|')
})
test('mystery bug #6', async () => {
strictEqual(await streamToString(Readable.from([
'One two one\n',
'One two one\n',
'One two one two\n',
'One two one two\n'
]).pipe(replace('two', 'three'))), [
'One three one\n',
'One three one\n',
'One three one three\n',
'One three one three\n'
].join(''))
})
const allTestsPassed = await test.run()
if (!allTestsPassed) process.exitCode = 1