Skip to content
Merged
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
34 changes: 33 additions & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,38 @@
name: Node.js
uses: node-modules/github-actions/.github/workflows/node-test.yml@master
with:
version: '16, 18, 20, 22, 24'
version: '18, 20, 22, 24'
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

Job-Node16:
name: Node.js 16
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
- name: Checkout Git Source
uses: actions/checkout@v4

- name: Use Node.js 16
uses: actions/setup-node@v4
with:
node-version: 16
check-latest: true

- name: Install Dependencies
run: npm i --no-package-lock --no-fund

- name: Build
run: npm run prepublishOnly

- name: Run Test
run: npx egg-bin cov

- name: Code Coverage
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
Comment on lines +20 to +49

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 6 hours ago

In general, this problem is fixed by adding an explicit permissions block either at the top level of the workflow (to apply to all jobs) or inside individual jobs, specifying the minimal access that GITHUB_TOKEN needs. For typical CI steps that only check out code, install dependencies, build, test, and send coverage to an external service with its own token, contents: read is sufficient.

For this workflow, the simplest, non‑breaking change is to add a root‑level permissions block directly under name: CI, before the on: key. This will apply to both Job (the reusable workflow call) and Job-Node16 unless they specify their own permissions. Since none of the visible steps require write access to the repository or other scopes, we can safely set:

permissions:
  contents: read

No additional imports, methods, or definitions are needed, as this is purely a YAML configuration change within .github/workflows/nodejs.yml.

Suggested changeset 1
.github/workflows/nodejs.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml
--- a/.github/workflows/nodejs.yml
+++ b/.github/workflows/nodejs.yml
@@ -1,5 +1,8 @@
 name: CI
 
+permissions:
+  contents: read
+
 on:
   push:
     branches: [ master ]
EOF
@@ -1,5 +1,8 @@
name: CI

permissions:
contents: read

on:
push:
branches: [ master ]
Copilot is powered by AI and may make mistakes. Always verify output.
5 changes: 4 additions & 1 deletion .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
"unicorn/prefer-number-properties": "allow",
"unicorn/prefer-string-slice": "allow",
"jsdoc/check-tag-names": "allow",
"jsdoc/no-defaults": "allow"
"jsdoc/no-defaults": "allow",
"import/no-named-export": "allow",
"import/no-nodejs-modules": "allow",
"import/no-relative-parent-imports": "allow"
}
}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)

[npm-image]: https://img.shields.io/npm/v/utility.svg?style=flat-square
[npm-url]: https://npmjs.org/package/utility
[npm-url]: https://npmx.dev/package/utility
[codecov-image]: https://codecov.io/github/node-modules/utility/coverage.svg?branch=master
[codecov-url]: https://codecov.io/github/node-modules/utility?branch=master
[download-image]: https://img.shields.io/npm/dm/utility.svg?style=flat-square
[download-url]: https://npmjs.org/package/utility
[download-url]: https://npmx.dev/package/utility

A collection of useful utilities.

Expand Down
15 changes: 4 additions & 11 deletions src/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,13 @@ export function randomSlice<T = any>(arr: T[], num?: number): T[] {
* @return {Array} the array instance
*/
export function spliceOne<T = any>(arr: T[], index: number): T[] {
if (index < 0) {
index = arr.length + index;
// still negative, not found element
if (index < 0) {
return arr;
}
}

// don't touch
if (index >= arr.length) {
const idx = index < 0 ? arr.length + index : index;
// Not found or out of bounds
if (idx < 0 || idx >= arr.length) {
return arr;
}

for (let i = index, k = i + 1, n = arr.length; k < n; i += 1, k += 1) {
for (let i = idx, k = i + 1, n = arr.length; k < n; i += 1, k += 1) {
arr[i] = arr[k];
}
arr.pop();
Expand Down
71 changes: 31 additions & 40 deletions src/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,21 @@ type HashMethod = (method: string, data: HashInput, outputEncoding?: BinaryToTex

const nativeHash = 'hash' in crypto ? crypto.hash as HashMethod : null;

function sortObject(o: any) {
if (!o || Array.isArray(o) || typeof o !== 'object') {
return o;
}
const keys = Object.keys(o);
keys.sort();
const values: any[] = [];
for (const k of keys) {
values.push([ k, sortObject(o[k]) ]);
}
return values;
}

/**
* hash
* Hash
*
* @param {String} method hash method, e.g.: 'md5', 'sha1'
* @param {String|Buffer|ArrayBuffer|TypedArray|DataView|Object} s input value
Expand All @@ -16,27 +29,23 @@ const nativeHash = 'hash' in crypto ? crypto.hash as HashMethod : null;
* @public
*/
export function hash(method: string, s: HashInput, format?: BinaryToTextEncoding): string {
if (s instanceof ArrayBuffer) {
s = Buffer.from(s);
}
const isBuffer = Buffer.isBuffer(s) || ArrayBuffer.isView(s);
if (!isBuffer && typeof s === 'object') {
s = JSON.stringify(sortObject(s));
}
const input: HashInput = s instanceof ArrayBuffer ? Buffer.from(s) : s;
const isBuffer = Buffer.isBuffer(input) || ArrayBuffer.isView(input);
const processedInput = (!isBuffer && typeof input === 'object') ? JSON.stringify(sortObject(input)) : input;

if (nativeHash) {
// try to use crypto.hash first
// Try to use crypto.hash first
// https://nodejs.org/en/blog/release/v21.7.0#crypto-implement-cryptohash
return nativeHash(method, s, format);
return nativeHash(method, processedInput, format);
}

const sum = createHash(method);
sum.update(s as string, isBuffer ? 'binary' : 'utf8');
sum.update(processedInput as string, isBuffer ? 'binary' : 'utf8');
return sum.digest(format || 'hex');
}

/**
* md5 hash
* Md5 hash
*
* @param {String|Buffer|Object} s input value
* @param {String} [format] output string format, could be 'hex' or 'base64'. default is 'hex'.
Expand All @@ -48,7 +57,7 @@ export function md5(s: HashInput, format?: BinaryToTextEncoding): string {
}

/**
* sha1 hash
* Sha1 hash
*
* @param {String|Buffer|Object} s input value
* @param {String} [format] output string format, could be 'hex' or 'base64'. default is 'hex'.
Expand All @@ -60,7 +69,7 @@ export function sha1(s: HashInput, format?: BinaryToTextEncoding): string {
}

/**
* sha256 hash
* Sha256 hash
*
* @param {String|Buffer|Object} s input value
* @param {String} [format] output string format, could be 'hex' or 'base64'. default is 'hex'.
Expand All @@ -72,7 +81,7 @@ export function sha256(s: HashInput, format?: BinaryToTextEncoding): string {
}

/**
* sha512 hash
* Sha512 hash
*
* @param {String|Buffer|Object} s input value
* @param {String} [format] output string format, could be 'hex' or 'base64'. default is 'hex'.
Expand Down Expand Up @@ -101,10 +110,9 @@ export function sha512(s: HashInput, format?: BinaryToTextEncoding): string {
* @return {String} digest string.
*/
export function hmac(algorithm: string, key: string, data: string | Buffer, encoding?: BinaryToTextEncoding): string {
encoding = encoding || 'base64';
const hmac = createHmac(algorithm, key);
hmac.update(data as string, Buffer.isBuffer(data) ? 'binary' : 'utf8');
return hmac.digest(encoding);
const mac = createHmac(algorithm, key);
mac.update(data as string, Buffer.isBuffer(data) ? 'binary' : 'utf8');
return mac.digest(encoding || 'base64');
}

/**
Expand All @@ -116,10 +124,8 @@ export function hmac(algorithm: string, key: string, data: string | Buffer, enco
* @return {String} base64 encode format string.
*/
export function base64encode(s: string | Buffer, urlSafe?: boolean): string {
if (!Buffer.isBuffer(s)) {
s = Buffer.from(s);
}
let encode = s.toString('base64');
const buf = Buffer.isBuffer(s) ? s : Buffer.from(s);
let encode = buf.toString('base64');
if (urlSafe) {
encode = encode.replace(/\+/g, '-').replace(/\//g, '_');
}
Expand All @@ -136,25 +142,10 @@ export function base64encode(s: string | Buffer, urlSafe?: boolean): string {
* @return {String|Buffer} plain text.
*/
export function base64decode(encodeStr: string, urlSafe?: boolean, encoding?: BufferEncoding | 'buffer'): string | Buffer {
if (urlSafe) {
encodeStr = encodeStr.replace(/\-/g, '+').replace(/_/g, '/');
}
const buf = Buffer.from(encodeStr, 'base64');
const str = urlSafe ? encodeStr.replace(/\-/g, '+').replace(/_/g, '/') : encodeStr;
const buf = Buffer.from(str, 'base64');
if (encoding === 'buffer') {
return buf;
}
return buf.toString(encoding || 'utf8');
}

function sortObject(o: any) {
if (!o || Array.isArray(o) || typeof o !== 'object') {
return o;
}
const keys = Object.keys(o);
keys.sort();
const values: any[] = [];
for (const k of keys) {
values.push([ k, sortObject(o[k]) ]);
}
return values;
}
Loading
Loading