From 2acd5cd7ffdde2b1c687b6586f1398bf2608b084 Mon Sep 17 00:00:00 2001 From: Charles Opute Odili Date: Sun, 18 Feb 2024 12:20:37 -0700 Subject: [PATCH 1/2] Dry run release (#14) --- .github/workflows/CD.yaml | 9 +++-- .github/workflows/CI.yaml | 4 +++ .github/workflows/snyk.yml | 14 ++++++++ README.md | 2 +- __tests__/smoke.test.js | 2 +- dist/{ => cjs}/index.js | 0 dist/{ => cjs/types}/index.d.ts | 1 + dist/cjs/types/index.d.ts.map | 1 + dist/demo/demo-basics.js | 34 ------------------ dist/demo/demo-classic.js | 32 ----------------- dist/demo/demo-utils.js | 36 ------------------- dist/esm/index.mjs | 62 +++++++++++++++++++++++++++++++++ dist/esm/types/index.d.ts | 13 +++++++ dist/esm/types/index.d.ts.map | 1 + package.json | 56 +++++++++++++++++++++++------ tsconfig.base.json | 21 +++++++++++ tsconfig.cjs.json | 14 ++++++++ tsconfig.esm.json | 14 ++++++++ tsconfig.json | 23 +----------- 19 files changed, 201 insertions(+), 138 deletions(-) create mode 100644 .github/workflows/snyk.yml rename dist/{ => cjs}/index.js (100%) rename dist/{ => cjs/types}/index.d.ts (92%) create mode 100644 dist/cjs/types/index.d.ts.map delete mode 100644 dist/demo/demo-basics.js delete mode 100644 dist/demo/demo-classic.js delete mode 100644 dist/demo/demo-utils.js create mode 100644 dist/esm/index.mjs create mode 100644 dist/esm/types/index.d.ts create mode 100644 dist/esm/types/index.d.ts.map create mode 100644 tsconfig.base.json create mode 100644 tsconfig.cjs.json create mode 100644 tsconfig.esm.json diff --git a/.github/workflows/CD.yaml b/.github/workflows/CD.yaml index d7adac2..3966c02 100644 --- a/.github/workflows/CD.yaml +++ b/.github/workflows/CD.yaml @@ -1,10 +1,11 @@ -name: CD +name: Release + on: pull_request: types: [ closed ] jobs: - publish-to-registry: + release: if: github.event.pull_request.base.ref == 'dev' && github.event.pull_request.merged == true runs-on: ubuntu-latest @@ -12,6 +13,10 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - uses: jaywcjlove/github-action-package@main + with: + unset: scripts.prepare + - name: Publish to Registry uses: JS-DevTools/npm-publish@v3 with: diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index d355aee..369eac9 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -1,5 +1,9 @@ name: CI on: + push: + branches-ignore: + - "main" + - "dev" pull_request: branches: - "dev" diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml new file mode 100644 index 0000000..fb3f98f --- /dev/null +++ b/.github/workflows/snyk.yml @@ -0,0 +1,14 @@ +name: Security Check + +on: [pull_request] + +jobs: + security-audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check for Security Vulnerabilities + uses: snyk/actions/node@master + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} \ No newline at end of file diff --git a/README.md b/README.md index 391b1f5..9fcee3b 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ npm install @chalu/n-tuple-array ``` ```javascript -import { tuplesFromArray } from 'n-tuple-array'; +const { tuplesFromArray } = require('@chalu/n-tuple-array'); // some setup const numbers = Array.from({length: 100}, (_, i) => i + 1); diff --git a/__tests__/smoke.test.js b/__tests__/smoke.test.js index 5a81cb3..acd34ce 100644 --- a/__tests__/smoke.test.js +++ b/__tests__/smoke.test.js @@ -1,4 +1,4 @@ -const {tuplesFromArray, InvalidInvocationParameterError} = require('../dist/index.js'); +const {tuplesFromArray, InvalidInvocationParameterError} = require('../dist/cjs/index.js'); describe('Smoke Tests', () => { it('should throw if input array is not specified', () => { diff --git a/dist/index.js b/dist/cjs/index.js similarity index 100% rename from dist/index.js rename to dist/cjs/index.js diff --git a/dist/index.d.ts b/dist/cjs/types/index.d.ts similarity index 92% rename from dist/index.d.ts rename to dist/cjs/types/index.d.ts index aea6b25..cb5aa72 100644 --- a/dist/index.d.ts +++ b/dist/cjs/types/index.d.ts @@ -10,3 +10,4 @@ export declare class InvalidInvocationParameterError extends Error { } export declare const tuplesFromArray: (config: TupleConfig) => Iterable>; export default tuplesFromArray; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/cjs/types/index.d.ts.map b/dist/cjs/types/index.d.ts.map new file mode 100644 index 0000000..8d87987 --- /dev/null +++ b/dist/cjs/types/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;AAC7B,KAAK,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAG/B,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC;AACxD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC5B,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CACnB,CAAC;AAEF,qBAAa,+BAAgC,SAAQ,KAAK;CAAG;AAqB7D,eAAO,MAAM,eAAe,mDAmD3B,CAAC;AAEF,eAAe,eAAe,CAAC"} \ No newline at end of file diff --git a/dist/demo/demo-basics.js b/dist/demo/demo-basics.js deleted file mode 100644 index 6f01236..0000000 --- a/dist/demo/demo-basics.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const index_1 = require("../index"); -const demo_utils_1 = require("./demo-utils"); -console.log('----- Example 1 [10 Hex in twos. Used default param values] ------'); -for (const hexPair of (0, index_1.tuplesFromArray)({ list: (0, demo_utils_1.hexadecimals)(10) })) { - console.log(hexPair); -} -console.log('\n----- Example 2 [30 Hex in fives. Specified maxItems] ------'); -for (const hexQuintet of (0, index_1.tuplesFromArray)({ list: (0, demo_utils_1.hexadecimals)(30), maxItems: 5 })) { - console.log(hexQuintet); -} -console.log('\n----- Example 3 [Dates in twos. Filtered in by a match function] ------'); -// Create an array of 50 elements which include some dates -const data = [...(0, demo_utils_1.hexadecimals)(20), ...(0, demo_utils_1.dates)(10), ...(0, demo_utils_1.uuids)(20)]; -// Use a basic/weak shuffle algo to shuffle the array items -data.sort(() => Math.random() - 0.5); -for (const dateTriplet of (0, index_1.tuplesFromArray)({ list: data, match: demo_utils_1.isDate })) { - console.log(dateTriplet); -} -console.log('\n----- Example 4 [Tuples can be "remainders", see the last array/tuple] ------'); -for (const idTriplets of (0, index_1.tuplesFromArray)({ list: (0, demo_utils_1.uuids)(8), maxItems: 3 })) { - console.log(idTriplets); -} -const dozenHexValues = (0, demo_utils_1.hexadecimals)(12); -const hexIterable = (0, index_1.tuplesFromArray)({ list: dozenHexValues, maxItems: 2 }); -const hexPairs = Array.from(hexIterable); -console.log('Array.from( tuplesFromArray(...) ) is an Array:', Array.isArray(hexPairs)); -// Use native Array methods -hexPairs - .map(pair => pair.toString().toUpperCase().split(',')) - .forEach(pair => { - console.log(pair); -}); diff --git a/dist/demo/demo-classic.js b/dist/demo/demo-classic.js deleted file mode 100644 index cbcab33..0000000 --- a/dist/demo/demo-classic.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const index_1 = require("../index"); -const demo_utils_1 = require("./demo-utils"); -const processItem = async (item) => { - await (0, demo_utils_1.delay)(Math.random() * 500); // Simulate some async workload - const processed = (item instanceof Date && (0, demo_utils_1.isDate)(item)) ? demo_utils_1.canadaDateFormat.format(item) : item; - return processed; -}; -const parallelPool = async (tasks, prarallelSize) => { - const processed = []; - const parallelSizeAwareIterable = (0, index_1.tuplesFromArray)({ - list: tasks, - maxItems: prarallelSize, - }); - let count = 1; - for await (const cohort of parallelSizeAwareIterable) { - const results = await Promise.allSettled(cohort.map(async (itm) => processItem(itm))); - processed.push(...results); - console.log(`processed ${(0, demo_utils_1.formatCount)(count)} batch of <= ${prarallelSize} items`); - if ((count * prarallelSize) >= tasks.length) { - console.log('DONE!'); - } - count += 1; - } - return processed; -}; -(async () => { - const canadianDates = await parallelPool((0, demo_utils_1.dates)(12), 3); - console.log('-------------------'); - console.log(canadianDates); -})(); diff --git a/dist/demo/demo-utils.js b/dist/demo/demo-utils.js deleted file mode 100644 index ef25db0..0000000 --- a/dist/demo/demo-utils.js +++ /dev/null @@ -1,36 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.delay = exports.formatCount = exports.canadaDateFormat = exports.isDate = exports.hexadecimals = exports.dates = exports.uuids = void 0; -const faker_1 = require("@faker-js/faker"); -/** - * Some Helper Functions - */ -const factory = (generator, howMany = 20) => faker_1.simpleFaker.helpers.multiple(generator, { count: howMany }); -const uuids = (howMany) => factory(faker_1.simpleFaker.string.uuid, howMany); -exports.uuids = uuids; -const dates = (howMany) => factory(faker_1.simpleFaker.date.birthdate, howMany); -exports.dates = dates; -const hexadecimals = (howMany) => factory(faker_1.simpleFaker.string.hexadecimal, howMany); -exports.hexadecimals = hexadecimals; -const isDate = (date) => (date instanceof Date) && !Number.isNaN(date.getTime()); -exports.isDate = isDate; -exports.canadaDateFormat = new Intl.DateTimeFormat('en-CA', { - dateStyle: 'medium', -}); -const plural = new Intl.PluralRules('en-US', { type: 'ordinal' }); -const suffixes = new Map([ - ['one', 'st'], - ['two', 'nd'], - ['few', 'rd'], - ['other', 'th'], -]); -const formatCount = (n) => { - const rule = plural.select(n); - const suffix = suffixes.get(rule); - return `${n}${suffix}`; -}; -exports.formatCount = formatCount; -const delay = async (ms) => new Promise(resolve => { - setTimeout(resolve, ms); -}); -exports.delay = delay; diff --git a/dist/esm/index.mjs b/dist/esm/index.mjs new file mode 100644 index 0000000..927cbf0 --- /dev/null +++ b/dist/esm/index.mjs @@ -0,0 +1,62 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.tuplesFromArray = exports.InvalidInvocationParameterError = void 0; +class InvalidInvocationParameterError extends Error { +} +exports.InvalidInvocationParameterError = InvalidInvocationParameterError; +const validateParametersOrThrow = (list, maxItems, match) => { + if (!list || !Array.isArray(list)) { + throw new InvalidInvocationParameterError('expected list to be an array'); + } + if (typeof maxItems !== 'number' + || (typeof maxItems === 'number' && maxItems <= 0)) { + const message = 'expected maxItems (when provided) to be a positive integer (1 and above)'; + throw new InvalidInvocationParameterError(message); + } + if (match !== undefined && typeof match !== 'function') { + const message = 'expected match (when provided) to be a function'; + throw new InvalidInvocationParameterError(message); + } +}; +const tuplesFromArray = (config) => { + const { list, match, maxItems = 2 } = config; + validateParametersOrThrow(list, maxItems, match); + let cursor = 0; + const maxItemSize = Number.parseInt(`${maxItems}`, 10); + const proceedNext = () => { + const items = []; + if (cursor >= list.length) { + return { done: true, value: [] }; + } + const endIndex = match === undefined + // A match funtion was provided. Okay to run to array end + // or until we've matched maxItemSize elements + ? Math.min(cursor + maxItemSize, list.length) + // No match function was provided. We should run till we are + // out of items (list.length) or till we gotten the next set + // of maxItemSize items + : list.length; + while (cursor < endIndex) { + const item = list[cursor]; + cursor += 1; + if (match && !match(item)) { + continue; + } + items.push(item); + if (match && items.length === maxItemSize) { + break; + } + } + return { value: items, done: items.length === 0 }; + }; + const iterable = { + [Symbol.iterator]() { + return { + next: proceedNext, + }; + }, + }; + return iterable; +}; +exports.tuplesFromArray = tuplesFromArray; +exports.default = exports.tuplesFromArray; diff --git a/dist/esm/types/index.d.ts b/dist/esm/types/index.d.ts new file mode 100644 index 0000000..cb5aa72 --- /dev/null +++ b/dist/esm/types/index.d.ts @@ -0,0 +1,13 @@ +type Item = T | undefined; +type Value = Array>; +export type Matcher = (item: T | unknown) => boolean; +export type TupleConfig = { + list: T[]; + maxItems?: number; + match?: Matcher; +}; +export declare class InvalidInvocationParameterError extends Error { +} +export declare const tuplesFromArray: (config: TupleConfig) => Iterable>; +export default tuplesFromArray; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/esm/types/index.d.ts.map b/dist/esm/types/index.d.ts.map new file mode 100644 index 0000000..8d87987 --- /dev/null +++ b/dist/esm/types/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;AAC7B,KAAK,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAG/B,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC;AACxD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC5B,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CACnB,CAAC;AAEF,qBAAa,+BAAgC,SAAQ,KAAK;CAAG;AAqB7D,eAAO,MAAM,eAAe,mDAmD3B,CAAC;AAEF,eAAe,eAAe,CAAC"} \ No newline at end of file diff --git a/package.json b/package.json index 7224684..7b2c5cc 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,48 @@ { "name": "@chalu/n-tuple-array", "version": "0.0.1", - "description": "Get a specified amount of items when iterating over a JavaScript array, instead of just a single item that native arrays provide!", - "main": "dist/index.js", - "types": "dist/index.d.ts", + "description": "Get a specified amount of items when iterating over a JavaScript array, instead of just a single item that arrays provide!", + "main": "./dist/cjs/index.js", + "types": "./dist/cjs/types/index.d.ts", "files": [ - "/dist" + "dist/**/*" ], + "exports": { + ".": { + "import": { + "types": "./dist/esm/types/index.d.ts", + "default": "./dist/esm/index.mjs" + }, + "require": { + "types": "./dist/cjs/types/index.d.ts", + "default": "./dist/cjs/index.js" + } + } + }, "scripts": { - "build": "tsc", - "prebuild": "rm -rf dist/*", - "postbuild": "rm -rf dist/demo/*.d.*", + "build": "pnpm build:esm && pnpm build:cjs", + "prebuild": "rm -rf dist", + "build:esm": "tsc -p tsconfig.esm.json && mv dist/esm/index.js dist/esm/index.mjs", + "build:cjs": "tsc -p tsconfig.cjs.json", "lint": "xo $(git diff --name-only --diff-filter=d HEAD | grep -E '\\.(ts|js)$' | xargs)", "lint:fix": "xo --fix $(git diff --name-only --diff-filter=d HEAD | grep -E '\\.(ts|js)$' | xargs)", - "test": "jest --runInBand", + "test": "jest", "pretest": "pnpm lint && pnpm build", "test:ci": "jest --ci --config='./jest.config.ci.ts'", - "prepare": "husky" + "prepare": "husky", + "prepack": "pnpm build" }, - "keywords": ["array", "tuple", "arrays", "tuples", "iterables", "iterators", "symbol.iterator", "javascript", "typescript"], + "keywords": [ + "array", + "tuple", + "arrays", + "tuples", + "iterables", + "iterators", + "symbol.iterator", + "javascript", + "typescript" + ], "author": "Charles Odili ", "license": "MIT", "devDependencies": { @@ -32,5 +56,17 @@ "ts-node": "^10.9.2", "typescript": "^5.3.3", "xo": "^0.57.0" + }, + "release": { + "branches": [ + "main" + ] + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/chalu/n-tuple-array.git" } } diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..211db10 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "strict": true, + "rootDir": "./src", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "checkJs": true, + "allowJs": true, + "declaration": true, + "declarationMap": true, + "noUncheckedIndexedAccess": true, + "allowSyntheticDefaultImports": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "src/demo/**" + ] +} \ No newline at end of file diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json new file mode 100644 index 0000000..5d35037 --- /dev/null +++ b/tsconfig.cjs.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "lib": [ + "ES2022", + "DOM" + ], + "target": "ES2022", + "module": "CommonJS", + "moduleResolution": "Node", + "outDir": "dist/cjs", + "declarationDir": "dist/cjs/types" + } +} \ No newline at end of file diff --git a/tsconfig.esm.json b/tsconfig.esm.json new file mode 100644 index 0000000..8beeb6a --- /dev/null +++ b/tsconfig.esm.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "lib": [ + "ES2022", + "DOM" + ], + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist/esm", + "declarationDir": "dist/esm/types" + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index f86599f..166936b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,24 +1,3 @@ { - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - /* Language and Environment */ - "lib": ["ES2022", "dom"], - "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ /* Control what method is used to detect module-format JS files. */ - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - "rootDir": "./src", /* Specify the root folder within your source files. */ - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ /* Ensure that casing is correct in imports. */ - "moduleResolution": "node", - "resolveJsonModule": true, - /* Type Checking */ - "strict": true, /* Skip type checking all .d.ts files. */ - "noUncheckedIndexedAccess": true, - "skipLibCheck": true - }, - "include": [ - "src/**/*" - ] + "extends": "./tsconfig.esm.json" } \ No newline at end of file From d4dc2848fee8739c5048e98e8bc392926c1b8270 Mon Sep 17 00:00:00 2001 From: Charles Opute Odili Date: Sun, 18 Feb 2024 12:27:41 -0700 Subject: [PATCH 2/2] Attempt release from GHA (#16) --- .github/workflows/CD.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/CD.yaml b/.github/workflows/CD.yaml index 3966c02..8e04669 100644 --- a/.github/workflows/CD.yaml +++ b/.github/workflows/CD.yaml @@ -21,5 +21,4 @@ jobs: uses: JS-DevTools/npm-publish@v3 with: token: ${{ secrets.NPM_TOKEN }} - dry-run: true \ No newline at end of file