diff --git a/.github/workflows/codesee-arch-diagram.yml b/.github/workflows/codesee-arch-diagram.yml new file mode 100644 index 0000000..80f58e6 --- /dev/null +++ b/.github/workflows/codesee-arch-diagram.yml @@ -0,0 +1,23 @@ +# This workflow was added by CodeSee. Learn more at https://codesee.io/ +# This is v2.0 of this workflow file +on: + push: + branches: + - staging + pull_request_target: + types: [opened, synchronize, reopened] + +name: CodeSee + +permissions: read-all + +jobs: + codesee: + runs-on: ubuntu-latest + continue-on-error: true + name: Analyze the repo with CodeSee + steps: + - uses: Codesee-io/codesee-action@v2 + with: + codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }} + codesee-url: https://app.codesee.io diff --git a/benches/index.ts b/benches/index.ts new file mode 100644 index 0000000..1316bb8 --- /dev/null +++ b/benches/index.ts @@ -0,0 +1,47 @@ +#!/usr/bin/env ts-node + +import fs from 'fs'; +import path from 'path'; +import si from 'systeminformation'; +import tableNotIndexed from './table_not_indexed'; +import tableIndexed from './table_indexed'; +import tableDerived from './table_derived'; + +async function main(): Promise { + await fs.promises.mkdir(path.join(__dirname, 'results'), { recursive: true }); + await tableNotIndexed(); + await tableIndexed(); + await tableDerived(); + const resultFilenames = await fs.promises.readdir( + path.join(__dirname, 'results'), + ); + const metricsFile = await fs.promises.open( + path.join(__dirname, 'results', 'metrics.txt'), + 'w', + ); + let concatenating = false; + for (const resultFilename of resultFilenames) { + if (/.+_metrics\.txt$/.test(resultFilename)) { + const metricsData = await fs.promises.readFile( + path.join(__dirname, 'results', resultFilename), + ); + if (concatenating) { + await metricsFile.write('\n'); + } + await metricsFile.write(metricsData); + concatenating = true; + } + } + await metricsFile.close(); + const systemData = await si.get({ + cpu: '*', + osInfo: 'platform, distro, release, kernel, arch', + system: 'model, manufacturer', + }); + await fs.promises.writeFile( + path.join(__dirname, 'results', 'system.json'), + JSON.stringify(systemData, null, 2), + ); +} + +void main(); diff --git a/benches/results/metrics.txt b/benches/results/metrics.txt new file mode 100644 index 0000000..e69de29 diff --git a/benches/results/system.json b/benches/results/system.json new file mode 100644 index 0000000..d0a34e7 --- /dev/null +++ b/benches/results/system.json @@ -0,0 +1,41 @@ +{ + "cpu": { + "manufacturer": "AMD", + "brand": "Ryzen 7 2700X Eight-Core Processor", + "vendor": "AMD", + "family": "23", + "model": "8", + "stepping": "2", + "revision": "", + "voltage": "", + "speed": 3.7, + "speedMin": 2.2, + "speedMax": 3.7, + "governor": "performance", + "cores": 16, + "physicalCores": 8, + "performanceCores": 8, + "efficiencyCores": 0, + "processors": 1, + "socket": "", + "flags": "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb hw_pstate ssbd ibpb vmmcall fsgsbase bmi1 avx2 smep bmi2 rdseed adx smap clflushopt sha_ni xsaveopt xsavec xgetbv1 clzero irperf xsaveerptr arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif overflow_recov succor smca sev sev_es", + "virtualization": true, + "cache": { + "l1d": 262144, + "l1i": 524288, + "l2": 4194304, + "l3": 16777216 + } + }, + "osInfo": { + "platform": "linux", + "distro": "nixos", + "release": "22.11", + "kernel": "6.1.23", + "arch": "x64" + }, + "system": { + "model": "System Product Name", + "manufacturer": "System manufacturer" + } +} \ No newline at end of file diff --git a/benches/table_derived.ts b/benches/table_derived.ts new file mode 100644 index 0000000..f21136b --- /dev/null +++ b/benches/table_derived.ts @@ -0,0 +1,41 @@ +import path from 'path'; +import b from 'benny'; +import Table from '@'; +import { suiteCommon } from './utils'; + +async function main() { + const summary = await b.suite( + path.basename(__filename, path.extname(__filename)), + b.add('insert', () => { + const t = new Table(['a', 'b', 'c', 'd'], [['a', 'b', 'c', 'd']]); + return () => { + t.insertRow({ a: 1, b: 2, c: 3, d: 4 }); + }; + }), + b.add('delete row', () => { + const t = new Table(['a', 'b', 'c', 'd'], [['a', 'b', 'c', 'd']]); + const rI = t.insertRow({ a: 1, b: 2, c: 3, d: 4 }); + return () => { + t.deleteRow(rI); + }; + }), + b.add('get where rows - 100', () => { + const t = new Table(['a', 'b', 'c', 'd'], [['a', 'b', 'c', 'd']]); + for (let i = 0; i < 100; i++) { + t.insertRow({ a: 1, b: 2, c: 3, d: 4 }); + } + return () => { + const rIs = t.whereRows(['a', 'b', 'c', 'd'], [1, 2, 3, 4]); + rIs.map((rI) => t.getRow(rI)); + }; + }), + ...suiteCommon, + ); + return summary; +} + +if (require.main === module) { + void main(); +} + +export default main; diff --git a/benches/table_indexed.ts b/benches/table_indexed.ts new file mode 100644 index 0000000..e7faf4a --- /dev/null +++ b/benches/table_indexed.ts @@ -0,0 +1,41 @@ +import path from 'path'; +import b from 'benny'; +import Table from '@'; +import { suiteCommon } from './utils'; + +async function main() { + const summary = await b.suite( + path.basename(__filename, path.extname(__filename)), + b.add('insert', () => { + const t = new Table(['a', 'b', 'c', 'd'], ['a', 'b', 'c', 'd']); + return () => { + t.insertRow({ a: 1, b: 2, c: 3, d: 4 }); + }; + }), + b.add('delete row', () => { + const t = new Table(['a', 'b', 'c', 'd'], ['a', 'b', 'c', 'd']); + const rI = t.insertRow({ a: 1, b: 2, c: 3, d: 4 }); + return () => { + t.deleteRow(rI); + }; + }), + b.add('get where rows - 100', () => { + const t = new Table(['a', 'b', 'c', 'd'], ['a', 'b', 'c', 'd']); + for (let i = 0; i < 100; i++) { + t.insertRow({ a: 1, b: 2, c: 3, d: 4 }); + } + return () => { + const rIs = t.whereRows('a', 1); + rIs.map((rI) => t.getRow(rI)); + }; + }), + ...suiteCommon, + ); + return summary; +} + +if (require.main === module) { + void main(); +} + +export default main; diff --git a/benches/table_not_indexed.ts b/benches/table_not_indexed.ts new file mode 100644 index 0000000..7d012df --- /dev/null +++ b/benches/table_not_indexed.ts @@ -0,0 +1,41 @@ +import path from 'path'; +import b from 'benny'; +import Table from '@'; +import { suiteCommon } from './utils'; + +async function main() { + const summary = await b.suite( + path.basename(__filename, path.extname(__filename)), + b.add('insert row', () => { + const t = new Table(['a', 'b', 'c', 'd']); + return () => { + t.insertRow({ a: 1, b: 2, c: 3, d: 4 }); + }; + }), + b.add('delete row', () => { + const t = new Table(['a', 'b', 'c', 'd']); + const rI = t.insertRow({ a: 1, b: 2, c: 3, d: 4 }); + return () => { + t.deleteRow(rI); + }; + }), + b.add('get where rows - 100', () => { + const t = new Table(['a', 'b', 'c', 'd']); + for (let i = 0; i < 100; i++) { + t.insertRow({ a: 1, b: 2, c: 3, d: 4 }); + } + return () => { + const rIs = t.whereRows('a', 1, true); + rIs.map((rI) => t.getRow(rI)); + }; + }), + ...suiteCommon, + ); + return summary; +} + +if (require.main === module) { + void main(); +} + +export default main; diff --git a/benches/utils.ts b/benches/utils.ts new file mode 100644 index 0000000..f3d4d95 --- /dev/null +++ b/benches/utils.ts @@ -0,0 +1,61 @@ +import fs from 'fs'; +import path from 'path'; +import b from 'benny'; +import { codeBlock } from 'common-tags'; +import packageJson from '../package.json'; + +const suiteCommon = [ + b.cycle(), + b.complete(), + b.save({ + file: (summary) => summary.name, + folder: path.join(__dirname, '../results'), + version: packageJson.version, + details: true, + }), + b.save({ + file: (summary) => summary.name, + folder: path.join(__dirname, '../results'), + version: packageJson.version, + format: 'chart.html', + }), + b.complete((summary) => { + const filePath = path.join( + __dirname, + '../results', + summary.name + '_metrics.txt', + ); + fs.writeFileSync( + filePath, + codeBlock` + # TYPE ${summary.name}_ops gauge + ${summary.results + .map( + (result) => + `${summary.name}_ops{name="${result.name}"} ${result.ops}`, + ) + .join('\n')} + + # TYPE ${summary.name}_margin gauge + ${summary.results + .map( + (result) => + `${summary.name}_margin{name="${result.name}"} ${result.margin}`, + ) + .join('\n')} + + # TYPE ${summary.name}_samples counter + ${summary.results + .map( + (result) => + `${summary.name}_samples{name="${result.name}"} ${result.samples}`, + ) + .join('\n')} + ` + '\n', + ); + // eslint-disable-next-line no-console + console.log('\nSaved to:', path.resolve(filePath)); + }), +]; + +export { suiteCommon }; diff --git a/package-lock.json b/package-lock.json index 68e27f2..0a6e6ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,8 @@ "@types/node": "^18.15.0", "@typescript-eslint/eslint-plugin": "^5.45.1", "@typescript-eslint/parser": "^5.45.1", + "benny": "^3.7.1", + "common-tags": "^1.8.2", "eslint": "^8.15.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", @@ -29,6 +31,7 @@ "jest-junit": "^14.0.0", "prettier": "^2.6.2", "shx": "^0.3.4", + "systeminformation": "^5.18.5", "ts-jest": "^28.0.5", "ts-node": "^10.9.1", "tsconfig-paths": "^3.9.0", @@ -49,6 +52,48 @@ "node": ">=6.0.0" } }, + "node_modules/@arrows/array": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@arrows/array/-/array-1.4.1.tgz", + "integrity": "sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==", + "dev": true, + "dependencies": { + "@arrows/composition": "^1.2.2" + } + }, + "node_modules/@arrows/composition": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@arrows/composition/-/composition-1.2.2.tgz", + "integrity": "sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==", + "dev": true + }, + "node_modules/@arrows/dispatch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@arrows/dispatch/-/dispatch-1.0.3.tgz", + "integrity": "sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==", + "dev": true, + "dependencies": { + "@arrows/composition": "^1.2.2" + } + }, + "node_modules/@arrows/error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@arrows/error/-/error-1.0.2.tgz", + "integrity": "sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==", + "dev": true + }, + "node_modules/@arrows/multimethod": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@arrows/multimethod/-/multimethod-1.4.1.tgz", + "integrity": "sha512-AZnAay0dgPnCJxn3We5uKiB88VL+1ZIF2SjZohLj6vqY2UyvB/sKdDnFP+LZNVsTC5lcnGPmLlRRkAh4sXkXsQ==", + "dev": true, + "dependencies": { + "@arrows/array": "^1.4.1", + "@arrows/composition": "^1.2.2", + "@arrows/error": "^1.0.2", + "fast-deep-equal": "^3.1.3" + } + }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -2013,6 +2058,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/babel-jest": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", @@ -2119,6 +2173,45 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/benchmark": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", + "integrity": "sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==", + "dev": true, + "dependencies": { + "lodash": "^4.17.4", + "platform": "^1.3.3" + } + }, + "node_modules/benny": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/benny/-/benny-3.7.1.tgz", + "integrity": "sha512-USzYxODdVfOS7JuQq/L0naxB788dWCiUgUTxvN+WLPt/JfcDURNNj8kN/N+uK6PDvuR67/9/55cVKGPleFQINA==", + "dev": true, + "dependencies": { + "@arrows/composition": "^1.0.0", + "@arrows/dispatch": "^1.0.2", + "@arrows/multimethod": "^1.1.6", + "benchmark": "^2.1.4", + "common-tags": "^1.8.0", + "fs-extra": "^10.0.0", + "json2csv": "^5.0.6", + "kleur": "^4.1.4", + "log-update": "^4.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/benny/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/bitset": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bitset/-/bitset-5.1.1.tgz", @@ -2288,6 +2381,18 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -2333,6 +2438,24 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3130,6 +3253,20 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4571,6 +4708,25 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "node_modules/json2csv": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-5.0.7.tgz", + "integrity": "sha512-YRZbUnyaJZLZUJSRi2G/MqahCyRv9n/ds+4oIetjDF3jWQA7AG7iSeKTiZiCNqtMZM7HDyt0e/W6lEnoGEmMGA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "dependencies": { + "commander": "^6.1.0", + "jsonparse": "^1.3.1", + "lodash.get": "^4.4.2" + }, + "bin": { + "json2csv": "bin/json2csv.js" + }, + "engines": { + "node": ">= 10", + "npm": ">= 6.13.0" + } + }, "node_modules/json5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", @@ -4589,6 +4745,27 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -4639,6 +4816,18 @@ "node": ">=4" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -4651,6 +4840,38 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -5146,6 +5367,12 @@ "node": ">=8" } }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "dev": true + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5396,6 +5623,19 @@ "node": ">=6.4.0" } }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5566,6 +5806,23 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5746,6 +6003,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/systeminformation": { + "version": "5.18.6", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.18.6.tgz", + "integrity": "sha512-pLXv6kjJZ1xUcVs9SrCqbQ9y0x1rgRWxBUc8/KxpOp9IRxFGFfzVK5efsxBn/KdYog4C9rPcKk+kHNIL2SB/8Q==", + "dev": true, + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -6055,6 +6338,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", diff --git a/package.json b/package.json index 306f689..44f77a3 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "lint": "eslint '{src,tests,scripts,benches}/**/*.{js,ts}'", "lintfix": "eslint '{src,tests,scripts,benches}/**/*.{js,ts}' --fix", "lint-shell": "find ./src ./tests ./scripts -type f -regextype posix-extended -regex '.*\\.(sh)' -exec shellcheck {} +", - "docs": "shx rm -rf ./docs && typedoc --gitRevision master --tsconfig ./tsconfig.build.json --out ./docs src" + "docs": "shx rm -rf ./docs && typedoc --gitRevision master --tsconfig ./tsconfig.build.json --out ./docs src", + "bench": "shx rm -rf ./benches/results && ts-node ./benches" }, "dependencies": { "resource-counter": "^1.2.4" @@ -32,6 +33,8 @@ "@types/node": "^18.15.0", "@typescript-eslint/eslint-plugin": "^5.45.1", "@typescript-eslint/parser": "^5.45.1", + "benny": "^3.7.1", + "common-tags": "^1.8.2", "eslint": "^8.15.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", @@ -42,6 +45,7 @@ "jest-junit": "^14.0.0", "prettier": "^2.6.2", "shx": "^0.3.4", + "systeminformation": "^5.18.5", "ts-jest": "^28.0.5", "ts-node": "^10.9.1", "tsconfig-paths": "^3.9.0", diff --git a/src/Table.ts b/src/Table.ts index 5a9fa8b..78cb89f 100644 --- a/src/Table.ts +++ b/src/Table.ts @@ -42,6 +42,7 @@ class Table> { indexes: Array< | keyof R | [keyof R, (v: any) => any] + | Array | [Array, (...vs: Array) => any] > = [], ) { @@ -57,13 +58,32 @@ class Table> { this.indexes[i] = { index: new Map(), }; - } else if (Array.isArray(i) && !Array.isArray(i[0])) { + } else if ( + Array.isArray(i) && + typeof i[0] === 'string' && + typeof i[1] === 'function' + ) { const [k, f] = i; this.indexes[k as string] = { index: new Map(), f, }; - } else if (Array.isArray(i) && Array.isArray(i[0])) { + } else if (Array.isArray(i) && typeof i[0] === 'string') { + const kDerived = i.join(''); + this.indexesDerived[kDerived] = { + index: new Map(), + deps: i as Array, + }; + for (const k of i) { + const kDerivedSet = (this.indexesDerivedDeps[k as string] ??= + new Set()); + kDerivedSet.add(kDerived); + } + } else if ( + Array.isArray(i) && + Array.isArray(i[0]) && + typeof i[1] === 'function' + ) { const [ks, f] = i; const kDerived = ks.join(''); this.indexesDerived[kDerived] = { @@ -180,10 +200,11 @@ class Table> { throw new RangeError(`Key \`${k}\` does not exist`); } const index = this.indexes[k]; - const v_ = index.f != null ? index.f(v) : v.toString(); if (index != null) { + const v_ = index.f != null ? index.f(v) : v.toString(); return [...(index.index.get(v_) ?? new Set())]; } else if (search) { + const v_ = v.toString(); const rIs: Array = []; for (const [rI, r] of this.rows.entries()) { if (r[k] === v_) { @@ -200,11 +221,12 @@ class Table> { } const kDerived = k.join(''); const index = this.indexesDerived[kDerived]; - const v_ = - index.f != null ? index.f(...v) : v.map((v) => v.toString()).join(''); if (index != null) { + const v_ = + index.f != null ? index.f(...v) : v.map((v) => v.toString()).join(''); return [...(index.index.get(v_) ?? new Set())]; } else if (search) { + const v_ = v.map((v) => v.toString()).join(''); const rIs: Array = []; for (const [rI, r] of this.rows.entries()) { if (k.map((k) => r[k].toString()).join('') === v_) { diff --git a/src/index.ts b/src/index.ts index db51cef..320a54b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,2 @@ -export { default as Table } from './Table'; -export * as utils from './utils'; +export { default } from './Table'; +export * from './utils'; diff --git a/tests/Table.test.ts b/tests/Table.test.ts index 7779b55..3cff4d0 100644 --- a/tests/Table.test.ts +++ b/tests/Table.test.ts @@ -18,7 +18,7 @@ describe(Table.name, () => { expect(t.whereRows('a', 'bar')).toHaveLength(0); }); test('inserting, setting, updating rows use shallow copy', () => { - const t = new Table<{ a: number, b: number }>(['a', 'b']); + const t = new Table<{ a: number; b: number }>(['a', 'b']); const row1 = { a: 1, b: 2 }; const rI = t.insertRow(row1); row1.a = 3; @@ -39,7 +39,7 @@ describe(Table.name, () => { expect(row3_.b).toBe(3); }); test('get table uses shallow copy', () => { - const t = new Table<{ a: number, b: number }>(['a', 'b']); + const t = new Table<{ a: number; b: number }>(['a', 'b']); const table = t.getTable(); table.set(1, { a: 3, b: 4 }); const table_ = t.getTable(); diff --git a/tsconfig.build.json b/tsconfig.build.json index 3c5544c..724de44 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -7,6 +7,7 @@ }, "exclude": [ "./tests/**/*", - "./scripts/**/*" + "./scripts/**/*", + "./benches/**/*" ] } diff --git a/tsconfig.json b/tsconfig.json index a774740..907ed72 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,8 @@ "./src/**/*", "./src/**/*.json", "./tests/**/*", - "./scripts/**/*" + "./scripts/**/*", + "./benches/**/*" ], "ts-node": { "require": ["tsconfig-paths/register"],