diff --git a/coverage/base.css b/coverage/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/coverage/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/coverage/block-navigation.js b/coverage/block-navigation.js new file mode 100644 index 0000000..530d1ed --- /dev/null +++ b/coverage/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selector that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/coverage/clover.xml b/coverage/clover.xml new file mode 100644 index 0000000..17dc5a4 --- /dev/null +++ b/coverage/clover.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/coverage/coverage-final.json b/coverage/coverage-final.json new file mode 100644 index 0000000..6d5ee24 --- /dev/null +++ b/coverage/coverage-final.json @@ -0,0 +1,8 @@ +{"/home/anton/Projects/fluent/src/internal.ts": {"path":"/home/anton/Projects/fluent/src/internal.ts","statementMap":{"0":{"start":{"line":8,"column":21},"end":{"line":8,"column":null}},"1":{"start":{"line":14,"column":2},"end":{"line":14,"column":null}},"2":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"3":{"start":{"line":23,"column":1},"end":{"line":28,"column":null}},"4":{"start":{"line":24,"column":2},"end":{"line":28,"column":null}},"5":{"start":{"line":26,"column":19},"end":{"line":26,"column":34}}},"fnMap":{"0":{"name":"constructor","decl":{"start":{"line":13,"column":1},"end":{"line":13,"column":8}},"loc":{"start":{"line":13,"column":34},"end":{"line":16,"column":null}},"line":13},"1":{"name":"assert","decl":{"start":{"line":19,"column":16},"end":{"line":19,"column":null}},"loc":{"start":{"line":22,"column":21},"end":{"line":29,"column":null}},"line":22},"2":{"name":"(anonymous_2)","decl":{"start":{"line":26,"column":5},"end":{"line":26,"column":13}},"loc":{"start":{"line":26,"column":19},"end":{"line":26,"column":34}},"line":26},"3":{"name":"assertType","decl":{"start":{"line":32,"column":16},"end":{"line":32,"column":30}},"loc":{"start":{"line":32,"column":60},"end":{"line":34,"column":null}},"line":32}},"branchMap":{"0":{"loc":{"start":{"line":23,"column":1},"end":{"line":28,"column":null}},"type":"if","locations":[{"start":{"line":23,"column":1},"end":{"line":28,"column":null}},{"start":{},"end":{}}],"line":23}},"s":{"0":4,"1":0,"2":0,"3":53,"4":0,"5":0},"f":{"0":0,"1":53,"2":0,"3":125},"b":{"0":[0,53]},"meta":{"lastBranch":1,"lastFunction":4,"lastStatement":6,"seen":{"s:8:21:8:Infinity":0,"f:13:1:13:8":0,"s:14:2:14:Infinity":1,"s:15:2:15:Infinity":2,"f:19:16:19:Infinity":1,"b:23:1:28:Infinity:undefined:undefined:undefined:undefined":0,"s:23:1:28:Infinity":3,"s:24:2:28:Infinity":4,"f:26:5:26:13":2,"s:26:19:26:34":5,"f:32:16:32:30":3},"fnNames":{}}} +,"/home/anton/Projects/fluent/src/base/index.ts": {"path":"/home/anton/Projects/fluent/src/base/index.ts","statementMap":{"0":{"start":{"line":18,"column":7},"end":{"line":28,"column":null}},"1":{"start":{"line":20,"column":12},"end":{"line":20,"column":null}},"2":{"start":{"line":22,"column":2},"end":{"line":25,"column":null}},"3":{"start":{"line":24,"column":3},"end":{"line":24,"column":null}},"4":{"start":{"line":27,"column":2},"end":{"line":27,"column":null}},"5":{"start":{"line":30,"column":1},"end":{"line":30,"column":null}}},"fnMap":{"0":{"name":"makeFluent","decl":{"start":{"line":15,"column":16},"end":{"line":15,"column":null}},"loc":{"start":{"line":17,"column":2},"end":{"line":31,"column":null}},"line":17},"1":{"name":"(anonymous_1)","decl":{"start":{"line":18,"column":7},"end":{"line":18,"column":26}},"loc":{"start":{"line":18,"column":39},"end":{"line":28,"column":null}},"line":18}},"branchMap":{},"s":{"0":4,"1":66,"2":66,"3":71,"4":66,"5":4},"f":{"0":4,"1":66},"b":{},"meta":{"lastBranch":0,"lastFunction":2,"lastStatement":6,"seen":{"f:15:16:15:Infinity":0,"s:18:7:28:Infinity":0,"f:18:7:18:26":1,"s:20:12:20:Infinity":1,"s:22:2:25:Infinity":2,"s:24:3:24:Infinity":3,"s:27:2:27:Infinity":4,"s:30:1:30:Infinity":5},"fnNames":{}}} +,"/home/anton/Projects/fluent/src/base/mixin.ts": {"path":"/home/anton/Projects/fluent/src/base/mixin.ts","statementMap":{"0":{"start":{"line":24,"column":1},"end":{"line":27,"column":null}}},"fnMap":{"0":{"name":"Mixin","decl":{"start":{"line":23,"column":16},"end":{"line":23,"column":42}},"loc":{"start":{"line":23,"column":65},"end":{"line":28,"column":null}},"line":23}},"branchMap":{},"s":{"0":5},"f":{"0":5},"b":{},"meta":{"lastBranch":0,"lastFunction":1,"lastStatement":1,"seen":{"f:23:16:23:42":0,"s:24:1:27:Infinity":0},"fnNames":{}}} +,"/home/anton/Projects/fluent/src/mixin/array.ts": {"path":"/home/anton/Projects/fluent/src/mixin/array.ts","statementMap":{"0":{"start":{"line":133,"column":1},"end":{"line":133,"column":null}},"1":{"start":{"line":315,"column":13},"end":{"line":417,"column":null}},"2":{"start":{"line":316,"column":1},"end":{"line":316,"column":null}},"3":{"start":{"line":316,"column":22},"end":{"line":316,"column":null}},"4":{"start":{"line":318,"column":1},"end":{"line":320,"column":null}},"5":{"start":{"line":319,"column":2},"end":{"line":319,"column":null}},"6":{"start":{"line":322,"column":1},"end":{"line":325,"column":null}},"7":{"start":{"line":323,"column":2},"end":{"line":323,"column":null}},"8":{"start":{"line":324,"column":2},"end":{"line":324,"column":null}},"9":{"start":{"line":327,"column":1},"end":{"line":333,"column":null}},"10":{"start":{"line":328,"column":2},"end":{"line":328,"column":null}},"11":{"start":{"line":329,"column":2},"end":{"line":329,"column":null}},"12":{"start":{"line":329,"column":19},"end":{"line":329,"column":null}},"13":{"start":{"line":330,"column":2},"end":{"line":332,"column":null}},"14":{"start":{"line":335,"column":1},"end":{"line":337,"column":null}},"15":{"start":{"line":336,"column":2},"end":{"line":336,"column":null}},"16":{"start":{"line":339,"column":1},"end":{"line":341,"column":null}},"17":{"start":{"line":340,"column":2},"end":{"line":340,"column":null}},"18":{"start":{"line":342,"column":1},"end":{"line":342,"column":null}},"19":{"start":{"line":343,"column":1},"end":{"line":349,"column":null}},"20":{"start":{"line":345,"column":3},"end":{"line":347,"column":null}},"21":{"start":{"line":346,"column":24},"end":{"line":346,"column":53}},"22":{"start":{"line":351,"column":1},"end":{"line":356,"column":null}},"23":{"start":{"line":355,"column":2},"end":{"line":355,"column":null}},"24":{"start":{"line":357,"column":1},"end":{"line":357,"column":null}},"25":{"start":{"line":358,"column":1},"end":{"line":365,"column":null}},"26":{"start":{"line":363,"column":3},"end":{"line":363,"column":null}},"27":{"start":{"line":367,"column":1},"end":{"line":369,"column":null}},"28":{"start":{"line":368,"column":2},"end":{"line":368,"column":null}},"29":{"start":{"line":371,"column":1},"end":{"line":381,"column":null}},"30":{"start":{"line":378,"column":2},"end":{"line":380,"column":null}},"31":{"start":{"line":379,"column":28},"end":{"line":379,"column":49}},"32":{"start":{"line":382,"column":1},"end":{"line":382,"column":null}},"33":{"start":{"line":383,"column":1},"end":{"line":412,"column":null}},"34":{"start":{"line":385,"column":34},"end":{"line":387,"column":null}},"35":{"start":{"line":390,"column":3},"end":{"line":394,"column":null}},"36":{"start":{"line":392,"column":5},"end":{"line":392,"column":null}},"37":{"start":{"line":397,"column":34},"end":{"line":399,"column":null}},"38":{"start":{"line":402,"column":3},"end":{"line":402,"column":null}},"39":{"start":{"line":402,"column":42},"end":{"line":402,"column":57}},"40":{"start":{"line":405,"column":34},"end":{"line":407,"column":null}},"41":{"start":{"line":410,"column":3},"end":{"line":410,"column":null}},"42":{"start":{"line":410,"column":42},"end":{"line":410,"column":57}},"43":{"start":{"line":414,"column":1},"end":{"line":416,"column":null}},"44":{"start":{"line":415,"column":2},"end":{"line":415,"column":null}}},"fnMap":{"0":{"name":"isArray","decl":{"start":{"line":132,"column":9},"end":{"line":132,"column":17}},"loc":{"start":{"line":132,"column":54},"end":{"line":134,"column":null}},"line":132},"1":{"name":"(anonymous_1)","decl":{"start":{"line":315,"column":21},"end":{"line":315,"column":35}},"loc":{"start":{"line":315,"column":56},"end":{"line":417,"column":1}},"line":315},"2":{"name":"(anonymous_2)","decl":{"start":{"line":318,"column":3},"end":{"line":318,"column":9}},"loc":{"start":{"line":318,"column":27},"end":{"line":320,"column":null}},"line":318},"3":{"name":"(anonymous_3)","decl":{"start":{"line":322,"column":3},"end":{"line":322,"column":11}},"loc":{"start":{"line":322,"column":53},"end":{"line":325,"column":null}},"line":322},"4":{"name":"(anonymous_4)","decl":{"start":{"line":327,"column":3},"end":{"line":327,"column":13}},"loc":{"start":{"line":327,"column":31},"end":{"line":333,"column":null}},"line":327},"5":{"name":"(anonymous_5)","decl":{"start":{"line":335,"column":3},"end":{"line":335,"column":10}},"loc":{"start":{"line":335,"column":55},"end":{"line":337,"column":null}},"line":335},"6":{"name":"(anonymous_6)","decl":{"start":{"line":339,"column":3},"end":{"line":339,"column":13}},"loc":{"start":{"line":339,"column":58},"end":{"line":341,"column":null}},"line":339},"7":{"name":"(anonymous_7)","decl":{"start":{"line":344,"column":2},"end":{"line":344,"column":14}},"loc":{"start":{"line":344,"column":14},"end":{"line":348,"column":null}},"line":344},"8":{"name":"(anonymous_8)","decl":{"start":{"line":346,"column":10},"end":{"line":346,"column":18}},"loc":{"start":{"line":346,"column":24},"end":{"line":346,"column":53}},"line":346},"9":{"name":"(anonymous_9)","decl":{"start":{"line":351,"column":3},"end":{"line":351,"column":null}},"loc":{"start":{"line":354,"column":6},"end":{"line":356,"column":null}},"line":354},"10":{"name":"(anonymous_10)","decl":{"start":{"line":359,"column":2},"end":{"line":359,"column":null}},"loc":{"start":{"line":362,"column":7},"end":{"line":364,"column":null}},"line":362},"11":{"name":"(anonymous_11)","decl":{"start":{"line":367,"column":3},"end":{"line":367,"column":18}},"loc":{"start":{"line":367,"column":18},"end":{"line":369,"column":null}},"line":367},"12":{"name":"(anonymous_12)","decl":{"start":{"line":371,"column":3},"end":{"line":371,"column":null}},"loc":{"start":{"line":377,"column":6},"end":{"line":381,"column":null}},"line":377},"13":{"name":"(anonymous_13)","decl":{"start":{"line":379,"column":9},"end":{"line":379,"column":19}},"loc":{"start":{"line":379,"column":28},"end":{"line":379,"column":49}},"line":379},"14":{"name":"(anonymous_14)","decl":{"start":{"line":384,"column":2},"end":{"line":384,"column":null}},"loc":{"start":{"line":389,"column":7},"end":{"line":395,"column":null}},"line":389},"15":{"name":"(anonymous_15)","decl":{"start":{"line":385,"column":3},"end":{"line":385,"column":34}},"loc":{"start":{"line":385,"column":34},"end":{"line":387,"column":null}},"line":385},"16":{"name":"(anonymous_16)","decl":{"start":{"line":391,"column":10},"end":{"line":391,"column":20}},"loc":{"start":{"line":392,"column":5},"end":{"line":392,"column":null}},"line":392},"17":{"name":"(anonymous_17)","decl":{"start":{"line":396,"column":2},"end":{"line":396,"column":null}},"loc":{"start":{"line":401,"column":7},"end":{"line":403,"column":null}},"line":401},"18":{"name":"(anonymous_18)","decl":{"start":{"line":397,"column":3},"end":{"line":397,"column":34}},"loc":{"start":{"line":397,"column":34},"end":{"line":399,"column":null}},"line":397},"19":{"name":"(anonymous_19)","decl":{"start":{"line":402,"column":23},"end":{"line":402,"column":33}},"loc":{"start":{"line":402,"column":42},"end":{"line":402,"column":57}},"line":402},"20":{"name":"(anonymous_20)","decl":{"start":{"line":404,"column":2},"end":{"line":404,"column":null}},"loc":{"start":{"line":409,"column":7},"end":{"line":411,"column":null}},"line":409},"21":{"name":"(anonymous_21)","decl":{"start":{"line":405,"column":3},"end":{"line":405,"column":34}},"loc":{"start":{"line":405,"column":34},"end":{"line":407,"column":null}},"line":405},"22":{"name":"(anonymous_22)","decl":{"start":{"line":410,"column":23},"end":{"line":410,"column":33}},"loc":{"start":{"line":410,"column":42},"end":{"line":410,"column":57}},"line":410},"23":{"name":"(anonymous_23)","decl":{"start":{"line":414,"column":3},"end":{"line":414,"column":19}},"loc":{"start":{"line":414,"column":19},"end":{"line":416,"column":null}},"line":414}},"branchMap":{"0":{"loc":{"start":{"line":133,"column":8},"end":{"line":133,"column":null}},"type":"binary-expr","locations":[{"start":{"line":133,"column":8},"end":{"line":133,"column":33}},{"start":{"line":133,"column":33},"end":{"line":133,"column":null}}],"line":133},"1":{"loc":{"start":{"line":316,"column":1},"end":{"line":316,"column":null}},"type":"if","locations":[{"start":{"line":316,"column":1},"end":{"line":316,"column":null}},{"start":{},"end":{}}],"line":316},"2":{"loc":{"start":{"line":329,"column":2},"end":{"line":329,"column":null}},"type":"if","locations":[{"start":{"line":329,"column":2},"end":{"line":329,"column":null}},{"start":{},"end":{}}],"line":329},"3":{"loc":{"start":{"line":346,"column":24},"end":{"line":346,"column":53}},"type":"binary-expr","locations":[{"start":{"line":346,"column":24},"end":{"line":346,"column":38}},{"start":{"line":346,"column":38},"end":{"line":346,"column":53}}],"line":346},"4":{"loc":{"start":{"line":385,"column":3},"end":{"line":387,"column":null}},"type":"default-arg","locations":[{"start":{"line":385,"column":3},"end":{"line":387,"column":null}}],"line":385},"5":{"loc":{"start":{"line":392,"column":5},"end":{"line":392,"column":null}},"type":"cond-expr","locations":[{"start":{"line":392,"column":25},"end":{"line":392,"column":30}},{"start":{"line":392,"column":30},"end":{"line":392,"column":null}}],"line":392},"6":{"loc":{"start":{"line":397,"column":3},"end":{"line":399,"column":null}},"type":"default-arg","locations":[{"start":{"line":397,"column":3},"end":{"line":399,"column":null}}],"line":397},"7":{"loc":{"start":{"line":405,"column":3},"end":{"line":407,"column":null}},"type":"default-arg","locations":[{"start":{"line":405,"column":3},"end":{"line":407,"column":null}}],"line":405}},"s":{"0":36,"1":1,"2":36,"3":7,"4":29,"5":4,"6":29,"7":1,"8":1,"9":29,"10":1,"11":1,"12":0,"13":1,"14":29,"15":1,"16":29,"17":1,"18":29,"19":29,"20":1,"21":5,"22":29,"23":1,"24":29,"25":29,"26":1,"27":29,"28":1,"29":29,"30":1,"31":8,"32":29,"33":29,"34":16,"35":1,"36":8,"37":18,"38":2,"39":11,"40":16,"41":2,"42":10,"43":29,"44":0},"f":{"0":36,"1":36,"2":4,"3":1,"4":1,"5":1,"6":1,"7":1,"8":5,"9":1,"10":1,"11":1,"12":1,"13":8,"14":1,"15":16,"16":8,"17":2,"18":18,"19":11,"20":2,"21":16,"22":10,"23":0},"b":{"0":[36,29],"1":[7,29],"2":[0,1],"3":[5,4],"4":[16],"5":[3,5],"6":[18],"7":[16]},"meta":{"lastBranch":8,"lastFunction":24,"lastStatement":45,"seen":{"f:132:9:132:17":0,"s:133:1:133:Infinity":0,"b:133:8:133:33:133:33:133:Infinity":0,"s:315:13:417:Infinity":1,"f:315:21:315:35":1,"b:316:1:316:Infinity:undefined:undefined:undefined:undefined":1,"s:316:1:316:Infinity":2,"s:316:22:316:Infinity":3,"s:318:1:320:Infinity":4,"f:318:3:318:9":2,"s:319:2:319:Infinity":5,"s:322:1:325:Infinity":6,"f:322:3:322:11":3,"s:323:2:323:Infinity":7,"s:324:2:324:Infinity":8,"s:327:1:333:Infinity":9,"f:327:3:327:13":4,"s:328:2:328:Infinity":10,"b:329:2:329:Infinity:undefined:undefined:undefined:undefined":2,"s:329:2:329:Infinity":11,"s:329:19:329:Infinity":12,"s:330:2:332:Infinity":13,"s:335:1:337:Infinity":14,"f:335:3:335:10":5,"s:336:2:336:Infinity":15,"s:339:1:341:Infinity":16,"f:339:3:339:13":6,"s:340:2:340:Infinity":17,"s:342:1:342:Infinity":18,"s:343:1:349:Infinity":19,"f:344:2:344:14":7,"s:345:3:347:Infinity":20,"f:346:10:346:18":8,"s:346:24:346:53":21,"b:346:24:346:38:346:38:346:53":3,"s:351:1:356:Infinity":22,"f:351:3:351:Infinity":9,"s:355:2:355:Infinity":23,"s:357:1:357:Infinity":24,"s:358:1:365:Infinity":25,"f:359:2:359:Infinity":10,"s:363:3:363:Infinity":26,"s:367:1:369:Infinity":27,"f:367:3:367:18":11,"s:368:2:368:Infinity":28,"s:371:1:381:Infinity":29,"f:371:3:371:Infinity":12,"s:378:2:380:Infinity":30,"f:379:9:379:19":13,"s:379:28:379:49":31,"s:382:1:382:Infinity":32,"s:383:1:412:Infinity":33,"f:384:2:384:Infinity":14,"b:385:3:387:Infinity":4,"f:385:3:385:34":15,"s:385:34:387:Infinity":34,"s:390:3:394:Infinity":35,"f:391:10:391:20":16,"s:392:5:392:Infinity":36,"b:392:25:392:30:392:30:392:Infinity":5,"f:396:2:396:Infinity":17,"b:397:3:399:Infinity":6,"f:397:3:397:34":18,"s:397:34:399:Infinity":37,"s:402:3:402:Infinity":38,"f:402:23:402:33":19,"s:402:42:402:57":39,"f:404:2:404:Infinity":20,"b:405:3:407:Infinity":7,"f:405:3:405:34":21,"s:405:34:407:Infinity":40,"s:410:3:410:Infinity":41,"f:410:23:410:33":22,"s:410:42:410:57":42,"s:414:1:416:Infinity":43,"f:414:3:414:19":23,"s:415:2:415:Infinity":44},"fnNames":{}}} +,"/home/anton/Projects/fluent/src/mixin/awaited.ts": {"path":"/home/anton/Projects/fluent/src/mixin/awaited.ts","statementMap":{"0":{"start":{"line":14,"column":2},"end":{"line":14,"column":null}},"1":{"start":{"line":18,"column":2},"end":{"line":18,"column":null}},"2":{"start":{"line":48,"column":13},"end":{"line":103,"column":null}},"3":{"start":{"line":49,"column":1},"end":{"line":49,"column":null}},"4":{"start":{"line":49,"column":34},"end":{"line":49,"column":null}},"5":{"start":{"line":51,"column":1},"end":{"line":53,"column":null}},"6":{"start":{"line":52,"column":2},"end":{"line":52,"column":null}},"7":{"start":{"line":55,"column":1},"end":{"line":102,"column":null}},"8":{"start":{"line":58,"column":41},"end":{"line":60,"column":null}},"9":{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},"10":{"start":{"line":62,"column":31},"end":{"line":62,"column":null}},"11":{"start":{"line":64,"column":17},"end":{"line":98,"column":null}},"12":{"start":{"line":64,"column":49},"end":{"line":64,"column":56}},"13":{"start":{"line":66,"column":5},"end":{"line":67,"column":null}},"14":{"start":{"line":67,"column":6},"end":{"line":67,"column":null}},"15":{"start":{"line":67,"column":27},"end":{"line":67,"column":34}},"16":{"start":{"line":68,"column":5},"end":{"line":68,"column":null}},"17":{"start":{"line":69,"column":5},"end":{"line":69,"column":null}},"18":{"start":{"line":72,"column":5},"end":{"line":95,"column":null}},"19":{"start":{"line":73,"column":25},"end":{"line":73,"column":null}},"20":{"start":{"line":74,"column":6},"end":{"line":85,"column":null}},"21":{"start":{"line":75,"column":7},"end":{"line":79,"column":null}},"22":{"start":{"line":80,"column":7},"end":{"line":82,"column":null}},"23":{"start":{"line":84,"column":7},"end":{"line":84,"column":null}},"24":{"start":{"line":87,"column":6},"end":{"line":87,"column":null}},"25":{"start":{"line":88,"column":6},"end":{"line":92,"column":null}},"26":{"start":{"line":94,"column":6},"end":{"line":94,"column":null}},"27":{"start":{"line":96,"column":5},"end":{"line":96,"column":null}},"28":{"start":{"line":100,"column":3},"end":{"line":100,"column":null}}},"fnMap":{"0":{"name":"constructor","decl":{"start":{"line":13,"column":1},"end":{"line":13,"column":8}},"loc":{"start":{"line":13,"column":30},"end":{"line":15,"column":null}},"line":13},"1":{"name":"(anonymous_1)","decl":{"start":{"line":17,"column":13},"end":{"line":17,"column":21}},"loc":{"start":{"line":17,"column":21},"end":{"line":23,"column":null}},"line":17},"2":{"name":"(anonymous_2)","decl":{"start":{"line":48,"column":26},"end":{"line":48,"column":45}},"loc":{"start":{"line":48,"column":66},"end":{"line":103,"column":1}},"line":48},"3":{"name":"(anonymous_3)","decl":{"start":{"line":51,"column":3},"end":{"line":51,"column":11}},"loc":{"start":{"line":51,"column":53},"end":{"line":53,"column":null}},"line":51},"4":{"name":"get","decl":{"start":{"line":57,"column":2},"end":{"line":57,"column":8}},"loc":{"start":{"line":57,"column":8},"end":{"line":101,"column":null}},"line":57},"5":{"name":"(anonymous_5)","decl":{"start":{"line":58,"column":47},"end":{"line":58,"column":53}},"loc":{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},"line":59},"6":{"name":"(anonymous_6)","decl":{"start":{"line":64,"column":21},"end":{"line":64,"column":28}},"loc":{"start":{"line":64,"column":49},"end":{"line":64,"column":56}},"line":64},"7":{"name":"(anonymous_7)","decl":{"start":{"line":65,"column":4},"end":{"line":65,"column":10}},"loc":{"start":{"line":65,"column":21},"end":{"line":70,"column":null}},"line":65},"8":{"name":"(anonymous_8)","decl":{"start":{"line":67,"column":15},"end":{"line":67,"column":21}},"loc":{"start":{"line":67,"column":27},"end":{"line":67,"column":34}},"line":67},"9":{"name":"(anonymous_9)","decl":{"start":{"line":71,"column":4},"end":{"line":71,"column":12}},"loc":{"start":{"line":71,"column":49},"end":{"line":97,"column":null}},"line":71},"10":{"name":"(anonymous_10)","decl":{"start":{"line":72,"column":11},"end":{"line":72,"column":17}},"loc":{"start":{"line":72,"column":23},"end":{"line":95,"column":6}},"line":72}},"branchMap":{"0":{"loc":{"start":{"line":49,"column":1},"end":{"line":49,"column":null}},"type":"if","locations":[{"start":{"line":49,"column":1},"end":{"line":49,"column":null}},{"start":{},"end":{}}],"line":49},"1":{"loc":{"start":{"line":66,"column":5},"end":{"line":67,"column":null}},"type":"if","locations":[{"start":{"line":66,"column":5},"end":{"line":67,"column":null}},{"start":{},"end":{}}],"line":66},"2":{"loc":{"start":{"line":76,"column":8},"end":{"line":78,"column":null}},"type":"binary-expr","locations":[{"start":{"line":76,"column":8},"end":{"line":76,"column":null}},{"start":{"line":77,"column":9},"end":{"line":77,"column":null}},{"start":{"line":78,"column":9},"end":{"line":78,"column":null}}],"line":76}},"s":{"0":0,"1":0,"2":1,"3":5,"4":2,"5":3,"6":1,"7":3,"8":1,"9":1,"10":1,"11":1,"12":1,"13":2,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1},"f":{"0":0,"1":0,"2":5,"3":1,"4":1,"5":1,"6":1,"7":2,"8":1,"9":1,"10":1},"b":{"0":[2,3],"1":[1,1],"2":[1,1,1]},"meta":{"lastBranch":3,"lastFunction":11,"lastStatement":29,"seen":{"f:13:1:13:8":0,"s:14:2:14:Infinity":0,"f:17:13:17:21":1,"s:18:2:18:Infinity":1,"s:48:13:103:Infinity":2,"f:48:26:48:45":2,"b:49:1:49:Infinity:undefined:undefined:undefined:undefined":0,"s:49:1:49:Infinity":3,"s:49:34:49:Infinity":4,"s:51:1:53:Infinity":5,"f:51:3:51:11":3,"s:52:2:52:Infinity":6,"s:55:1:102:Infinity":7,"f:57:2:57:8":4,"s:58:41:60:Infinity":8,"f:58:47:58:53":5,"s:59:4:59:Infinity":9,"s:62:31:62:Infinity":10,"s:64:17:98:Infinity":11,"f:64:21:64:28":6,"s:64:49:64:56":12,"f:65:4:65:10":7,"b:66:5:67:Infinity:undefined:undefined:undefined:undefined":1,"s:66:5:67:Infinity":13,"s:67:6:67:Infinity":14,"f:67:15:67:21":8,"s:67:27:67:34":15,"s:68:5:68:Infinity":16,"s:69:5:69:Infinity":17,"f:71:4:71:12":9,"s:72:5:95:Infinity":18,"f:72:11:72:17":10,"s:73:25:73:Infinity":19,"s:74:6:85:Infinity":20,"s:75:7:79:Infinity":21,"b:76:8:76:Infinity:77:9:77:Infinity:78:9:78:Infinity":2,"s:80:7:82:Infinity":22,"s:84:7:84:Infinity":23,"s:87:6:87:Infinity":24,"s:88:6:92:Infinity":25,"s:94:6:94:Infinity":26,"s:96:5:96:Infinity":27,"s:100:3:100:Infinity":28},"fnNames":{}}} +,"/home/anton/Projects/fluent/src/mixin/base.ts": {"path":"/home/anton/Projects/fluent/src/mixin/base.ts","statementMap":{"0":{"start":{"line":43,"column":13},"end":{"line":52,"column":null}},"1":{"start":{"line":44,"column":1},"end":{"line":47,"column":null}},"2":{"start":{"line":45,"column":2},"end":{"line":45,"column":null}},"3":{"start":{"line":46,"column":2},"end":{"line":46,"column":null}},"4":{"start":{"line":49,"column":1},"end":{"line":51,"column":null}},"5":{"start":{"line":50,"column":2},"end":{"line":50,"column":null}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":43,"column":20},"end":{"line":43,"column":33}},"loc":{"start":{"line":43,"column":54},"end":{"line":52,"column":1}},"line":43},"1":{"name":"(anonymous_1)","decl":{"start":{"line":44,"column":3},"end":{"line":44,"column":10}},"loc":{"start":{"line":44,"column":49},"end":{"line":47,"column":null}},"line":44},"2":{"name":"(anonymous_2)","decl":{"start":{"line":49,"column":3},"end":{"line":49,"column":16}},"loc":{"start":{"line":49,"column":58},"end":{"line":51,"column":null}},"line":49}},"branchMap":{},"s":{"0":2,"1":12,"2":1,"3":1,"4":12,"5":4},"f":{"0":12,"1":1,"2":4},"b":{},"meta":{"lastBranch":0,"lastFunction":3,"lastStatement":6,"seen":{"s:43:13:52:Infinity":0,"f:43:20:43:33":0,"s:44:1:47:Infinity":1,"f:44:3:44:10":1,"s:45:2:45:Infinity":2,"s:46:2:46:Infinity":3,"s:49:1:51:Infinity":4,"f:49:3:49:16":2,"s:50:2:50:Infinity":5},"fnNames":{}}} +,"/home/anton/Projects/fluent/src/mixin/optional.ts": {"path":"/home/anton/Projects/fluent/src/mixin/optional.ts","statementMap":{"0":{"start":{"line":16,"column":1},"end":{"line":16,"column":null}},"1":{"start":{"line":157,"column":13},"end":{"line":216,"column":null}},"2":{"start":{"line":158,"column":1},"end":{"line":207,"column":null}},"3":{"start":{"line":159,"column":2},"end":{"line":161,"column":null}},"4":{"start":{"line":160,"column":3},"end":{"line":160,"column":null}},"5":{"start":{"line":163,"column":2},"end":{"line":165,"column":null}},"6":{"start":{"line":164,"column":3},"end":{"line":164,"column":null}},"7":{"start":{"line":166,"column":2},"end":{"line":166,"column":null}},"8":{"start":{"line":167,"column":2},"end":{"line":171,"column":null}},"9":{"start":{"line":169,"column":4},"end":{"line":169,"column":null}},"10":{"start":{"line":173,"column":2},"end":{"line":175,"column":null}},"11":{"start":{"line":174,"column":3},"end":{"line":174,"column":null}},"12":{"start":{"line":176,"column":2},"end":{"line":176,"column":null}},"13":{"start":{"line":177,"column":2},"end":{"line":181,"column":null}},"14":{"start":{"line":179,"column":4},"end":{"line":179,"column":null}},"15":{"start":{"line":183,"column":2},"end":{"line":185,"column":null}},"16":{"start":{"line":184,"column":3},"end":{"line":184,"column":null}},"17":{"start":{"line":187,"column":2},"end":{"line":189,"column":null}},"18":{"start":{"line":188,"column":3},"end":{"line":188,"column":null}},"19":{"start":{"line":191,"column":2},"end":{"line":191,"column":null}},"20":{"start":{"line":192,"column":2},"end":{"line":196,"column":null}},"21":{"start":{"line":194,"column":4},"end":{"line":194,"column":null}},"22":{"start":{"line":198,"column":2},"end":{"line":200,"column":null}},"23":{"start":{"line":199,"column":3},"end":{"line":199,"column":null}},"24":{"start":{"line":201,"column":2},"end":{"line":201,"column":null}},"25":{"start":{"line":202,"column":2},"end":{"line":206,"column":null}},"26":{"start":{"line":204,"column":4},"end":{"line":204,"column":null}},"27":{"start":{"line":209,"column":1},"end":{"line":215,"column":null}},"28":{"start":{"line":213,"column":2},"end":{"line":213,"column":null}},"29":{"start":{"line":213,"column":23},"end":{"line":213,"column":null}},"30":{"start":{"line":214,"column":2},"end":{"line":214,"column":null}}},"fnMap":{"0":{"name":"isNone","decl":{"start":{"line":15,"column":9},"end":{"line":15,"column":19}},"loc":{"start":{"line":15,"column":39},"end":{"line":17,"column":null}},"line":15},"1":{"name":"(anonymous_1)","decl":{"start":{"line":157,"column":24},"end":{"line":157,"column":41}},"loc":{"start":{"line":157,"column":62},"end":{"line":216,"column":1}},"line":157},"2":{"name":"(anonymous_2)","decl":{"start":{"line":159,"column":4},"end":{"line":159,"column":16}},"loc":{"start":{"line":159,"column":16},"end":{"line":161,"column":null}},"line":159},"3":{"name":"(anonymous_3)","decl":{"start":{"line":163,"column":4},"end":{"line":163,"column":10}},"loc":{"start":{"line":163,"column":32},"end":{"line":165,"column":null}},"line":163},"4":{"name":"(anonymous_4)","decl":{"start":{"line":168,"column":3},"end":{"line":168,"column":10}},"loc":{"start":{"line":168,"column":48},"end":{"line":170,"column":null}},"line":168},"5":{"name":"(anonymous_5)","decl":{"start":{"line":173,"column":4},"end":{"line":173,"column":19}},"loc":{"start":{"line":173,"column":19},"end":{"line":175,"column":null}},"line":173},"6":{"name":"(anonymous_6)","decl":{"start":{"line":178,"column":3},"end":{"line":178,"column":10}},"loc":{"start":{"line":178,"column":27},"end":{"line":180,"column":null}},"line":178},"7":{"name":"(anonymous_7)","decl":{"start":{"line":183,"column":4},"end":{"line":183,"column":11}},"loc":{"start":{"line":183,"column":49},"end":{"line":185,"column":null}},"line":183},"8":{"name":"(anonymous_8)","decl":{"start":{"line":187,"column":4},"end":{"line":187,"column":15}},"loc":{"start":{"line":187,"column":15},"end":{"line":189,"column":null}},"line":187},"9":{"name":"(anonymous_9)","decl":{"start":{"line":193,"column":3},"end":{"line":193,"column":15}},"loc":{"start":{"line":193,"column":15},"end":{"line":195,"column":null}},"line":193},"10":{"name":"(anonymous_10)","decl":{"start":{"line":198,"column":4},"end":{"line":198,"column":14}},"loc":{"start":{"line":198,"column":31},"end":{"line":200,"column":null}},"line":198},"11":{"name":"(anonymous_11)","decl":{"start":{"line":203,"column":3},"end":{"line":203,"column":10}},"loc":{"start":{"line":203,"column":27},"end":{"line":205,"column":null}},"line":203},"12":{"name":"(anonymous_12)","decl":{"start":{"line":209,"column":3},"end":{"line":209,"column":null}},"loc":{"start":{"line":212,"column":6},"end":{"line":215,"column":null}},"line":212}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":8},"end":{"line":16,"column":null}},"type":"binary-expr","locations":[{"start":{"line":16,"column":8},"end":{"line":16,"column":22}},{"start":{"line":16,"column":22},"end":{"line":16,"column":null}}],"line":16},"1":{"loc":{"start":{"line":158,"column":1},"end":{"line":207,"column":null}},"type":"if","locations":[{"start":{"line":158,"column":1},"end":{"line":207,"column":null}},{"start":{"line":182,"column":8},"end":{"line":207,"column":null}}],"line":158},"2":{"loc":{"start":{"line":211,"column":2},"end":{"line":211,"column":null}},"type":"default-arg","locations":[{"start":{"line":211,"column":13},"end":{"line":211,"column":null}}],"line":211},"3":{"loc":{"start":{"line":213,"column":2},"end":{"line":213,"column":null}},"type":"if","locations":[{"start":{"line":213,"column":2},"end":{"line":213,"column":null}},{"start":{},"end":{}}],"line":213}},"s":{"0":18,"1":1,"2":18,"3":5,"4":1,"5":5,"6":1,"7":5,"8":5,"9":1,"10":5,"11":0,"12":5,"13":5,"14":0,"15":13,"16":1,"17":13,"18":1,"19":13,"20":13,"21":1,"22":13,"23":0,"24":13,"25":13,"26":0,"27":18,"28":3,"29":1,"30":2},"f":{"0":18,"1":18,"2":1,"3":1,"4":1,"5":0,"6":0,"7":1,"8":1,"9":1,"10":0,"11":0,"12":3},"b":{"0":[18,13],"1":[5,13],"2":[3],"3":[1,2]},"meta":{"lastBranch":4,"lastFunction":13,"lastStatement":31,"seen":{"f:15:9:15:19":0,"s:16:1:16:Infinity":0,"b:16:8:16:22:16:22:16:Infinity":0,"s:157:13:216:Infinity":1,"f:157:24:157:41":1,"b:158:1:207:Infinity:182:8:207:Infinity":1,"s:158:1:207:Infinity":2,"s:159:2:161:Infinity":3,"f:159:4:159:16":2,"s:160:3:160:Infinity":4,"s:163:2:165:Infinity":5,"f:163:4:163:10":3,"s:164:3:164:Infinity":6,"s:166:2:166:Infinity":7,"s:167:2:171:Infinity":8,"f:168:3:168:10":4,"s:169:4:169:Infinity":9,"s:173:2:175:Infinity":10,"f:173:4:173:19":5,"s:174:3:174:Infinity":11,"s:176:2:176:Infinity":12,"s:177:2:181:Infinity":13,"f:178:3:178:10":6,"s:179:4:179:Infinity":14,"s:183:2:185:Infinity":15,"f:183:4:183:11":7,"s:184:3:184:Infinity":16,"s:187:2:189:Infinity":17,"f:187:4:187:15":8,"s:188:3:188:Infinity":18,"s:191:2:191:Infinity":19,"s:192:2:196:Infinity":20,"f:193:3:193:15":9,"s:194:4:194:Infinity":21,"s:198:2:200:Infinity":22,"f:198:4:198:14":10,"s:199:3:199:Infinity":23,"s:201:2:201:Infinity":24,"s:202:2:206:Infinity":25,"f:203:3:203:10":11,"s:204:4:204:Infinity":26,"s:209:1:215:Infinity":27,"f:209:3:209:Infinity":12,"b:211:13:211:Infinity":2,"b:213:2:213:Infinity:undefined:undefined:undefined:undefined":3,"s:213:2:213:Infinity":28,"s:213:23:213:Infinity":29,"s:214:2:214:Infinity":30},"fnNames":{}}} +} diff --git a/coverage/favicon.png b/coverage/favicon.png new file mode 100644 index 0000000..c1525b8 Binary files /dev/null and b/coverage/favicon.png differ diff --git a/coverage/index.html b/coverage/index.html new file mode 100644 index 0000000..e692d01 --- /dev/null +++ b/coverage/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 90.32% + Statements + 112/124 +
+ + +
+ 93.1% + Branches + 27/29 +
+ + +
+ 84.48% + Functions + 49/58 +
+ + +
+ 90.51% + Lines + 105/116 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
src +
+
33.33%2/650%1/250%2/433.33%2/6
src/base +
+
100%7/7100%0/0100%3/3100%7/7
src/mixin +
+
92.79%103/11196.29%26/2786.27%44/5193.2%96/103
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/prettify.css b/coverage/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/coverage/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/coverage/prettify.js b/coverage/prettify.js new file mode 100644 index 0000000..b322523 --- /dev/null +++ b/coverage/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/coverage/sort-arrow-sprite.png b/coverage/sort-arrow-sprite.png new file mode 100644 index 0000000..6ed6831 Binary files /dev/null and b/coverage/sort-arrow-sprite.png differ diff --git a/coverage/sorter.js b/coverage/sorter.js new file mode 100644 index 0000000..4ed70ae --- /dev/null +++ b/coverage/sorter.js @@ -0,0 +1,210 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + + // Try to create a RegExp from the searchValue. If it fails (invalid regex), + // it will be treated as a plain text search + let searchRegex; + try { + searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive + } catch (error) { + searchRegex = null; + } + + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + let isMatch = false; + + if (searchRegex) { + // If a valid regex was created, use it for matching + isMatch = searchRegex.test(row.textContent); + } else { + // Otherwise, fall back to the original plain text search + isMatch = row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()); + } + + row.style.display = isMatch ? '' : 'none'; + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/coverage/src/base/index.html b/coverage/src/base/index.html new file mode 100644 index 0000000..63e2a24 --- /dev/null +++ b/coverage/src/base/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/base + + + + + + + + + +
+
+

All files src/base

+
+ +
+ 100% + Statements + 7/7 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 100% + Lines + 7/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
index.ts +
+
100%6/6100%0/0100%2/2100%6/6
mixin.ts +
+
100%1/1100%0/0100%1/1100%1/1
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/src/base/index.ts.html b/coverage/src/base/index.ts.html new file mode 100644 index 0000000..3db24ff --- /dev/null +++ b/coverage/src/base/index.ts.html @@ -0,0 +1,178 @@ + + + + + + Code coverage report for src/base/index.ts + + + + + + + + + +
+
+

All files / src/base index.ts

+
+ +
+ 100% + Statements + 6/6 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 6/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +4x +  +66x +  +66x +  +71x +  +  +66x +  +  +4x +  + 
import type { DefaultRegistry, Methods, Registry } from "../registry";
+import type { shim } from "./mixin";
+ 
+export type Base<T> = T extends { [shim]: { value: infer U } }
+	? { readonly value: U }
+	: {
+			readonly value: T;
+		};
+ 
+export type Fluent<
+	T,
+	Reg extends Registry = DefaultRegistry,
+> = Base<T> & Methods<T, Reg>;
+ 
+export function makeFluent<const Reg extends Registry>(
+	registry: Reg,
+) {
+	const fluent = <const T>(value: T) => {
+		// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
+		const f = { value } as never as Fluent<T, Reg>;
+ 
+		for (const mixin of registry) {
+			// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
+			mixin.fn(value, f, fluent as (value: unknown) => never);
+		}
+ 
+		return f;
+	};
+ 
+	return fluent;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/src/base/mixin.ts.html b/coverage/src/base/mixin.ts.html new file mode 100644 index 0000000..d84f94a --- /dev/null +++ b/coverage/src/base/mixin.ts.html @@ -0,0 +1,259 @@ + + + + + + Code coverage report for src/base/mixin.ts + + + + + + + + + +
+
+

All files / src/base mixin.ts

+
+ +
+ 100% + Statements + 1/1 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 1/1 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +5x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import type { Fluent } from ".";
+import { never } from "../internal";
+import type { Registry } from "../registry";
+import type { HKT } from "./hkt";
+ 
+export interface Props {
+	readonly value: unknown;
+	readonly meta: { registry: Registry };
+}
+ 
+type MixinHKT = HKT<Props>;
+type MixinFn = (
+	value: unknown,
+	fluent: Record<PropertyKey, unknown>,
+	callback: (value: unknown) => never,
+) => void;
+ 
+export interface Mixin<T extends MixinHKT = MixinHKT> {
+	hkt: T;
+	fn: MixinFn;
+}
+ 
+export function Mixin<T extends MixinHKT>(fn: MixinFn): Mixin<T> {
+	return {
+		hkt: never,
+		fn,
+	};
+}
+ 
+export namespace Mixin {
+	export type HKT = MixinHKT;
+	export type Function = MixinFn;
+}
+ 
+export declare const shim: unique symbol;
+export interface Shim {
+	input?: unknown;
+	output?: HKT;
+	value?: unknown;
+}
+ 
+export type Input<P extends Props> = P["value"] extends infer T
+	? T extends { [shim]: { input: infer I } }
+		? I
+		: T
+	: never;
+export type Return<T, P extends Props> = Fluent<
+	P["value"] extends { [shim]: { output: infer U extends HKT } }
+		? HKT.Apply<U, T>
+		: T,
+	P["meta"]["registry"]
+>;
+ 
+export type Instansiate<
+	M extends Mixin,
+	T,
+	Reg extends Registry,
+> = HKT.Apply<M["hkt"], { value: T; meta: { registry: Reg } }>;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/src/index.html b/coverage/src/index.html new file mode 100644 index 0000000..0f5a37e --- /dev/null +++ b/coverage/src/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src + + + + + + + + + +
+
+

All files src

+
+ +
+ 33.33% + Statements + 2/6 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 50% + Functions + 2/4 +
+ + +
+ 33.33% + Lines + 2/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
internal.ts +
+
33.33%2/650%1/250%2/433.33%2/6
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/src/internal.ts.html b/coverage/src/internal.ts.html new file mode 100644 index 0000000..1dc102f --- /dev/null +++ b/coverage/src/internal.ts.html @@ -0,0 +1,262 @@ + + + + + + Code coverage report for src/internal.ts + + + + + + + + + +
+
+

All files / src internal.ts

+
+ +
+ 33.33% + Statements + 2/6 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 50% + Functions + 2/4 +
+ + +
+ 33.33% + Lines + 2/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60  +  +  +  +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +53x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import type { HKT } from "./base/hkt";
+ 
+export interface Identity extends HKT {
+	new: (t: HKT.T<this>) => typeof t;
+}
+ 
+// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
+export const never = undefined as never;
+ 
+export type Constraint<T, U extends T> = U;
+ 
+export class AssertionError extends Error {
+	public constructor(msg?: string) {
+		super(msg);
+		this.name = "AssertionError";
+	}
+}
+ 
+export function assert(
+	condition: unknown,
+	msg?: string,
+): asserts condition {
+	Iif (!Boolean(condition))
+		throw new AssertionError(
+			["Assertion error", msg]
+				.filter((v) => v !== undefined)
+				.join(": "),
+		);
+}
+ 
+// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters, @typescript-eslint/no-unused-vars
+export function assertType<T>(_v: unknown): asserts _v is T {
+	/* empty */
+}
+ 
+// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
+type FunctionProto = Function;
+export interface HidePrototype {
+	/** @deprecated */
+	apply: FunctionProto["apply"];
+	/** @deprecated */
+	arguments: FunctionProto["arguments"];
+	/** @deprecated */
+	bind: FunctionProto["bind"];
+	/** @deprecated */
+	call: FunctionProto["call"];
+	/** @deprecated */
+	caller: FunctionProto["caller"];
+	/** @deprecated */
+	length: FunctionProto["length"];
+	/** @deprecated */
+	name: FunctionProto["name"];
+	/** @deprecated */
+	prototype: FunctionProto["prototype"];
+	/** @deprecated */
+	toString: FunctionProto["toString"];
+	/** @deprecated */
+	Symbol: SymbolConstructor;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/src/mixin/array.ts.html b/coverage/src/mixin/array.ts.html new file mode 100644 index 0000000..67dee75 --- /dev/null +++ b/coverage/src/mixin/array.ts.html @@ -0,0 +1,1834 @@ + + + + + + Code coverage report for src/mixin/array.ts + + + + + + + + + +
+
+

All files / src/mixin array.ts

+
+ +
+ 95.55% + Statements + 43/45 +
+ + +
+ 92.3% + Branches + 12/13 +
+ + +
+ 95.83% + Functions + 23/24 +
+ + +
+ 97.56% + Lines + 40/41 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +36x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +36x +  +29x +4x +  +  +29x +1x +1x +  +  +29x +1x +1x +1x +  +  +  +  +29x +1x +  +  +29x +1x +  +29x +29x +  +1x +5x +  +  +  +  +29x +  +  +  +1x +  +29x +29x +  +  +  +  +1x +  +  +  +29x +1x +  +  +29x +  +  +  +  +  +  +1x +8x +  +  +29x +29x +  +16x +  +  +  +  +1x +  +8x +  +  +  +  +18x +  +  +  +  +11x +  +  +16x +  +  +  +  +10x +  +  +  +29x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { makeFluent } from "../base";
+import type { HKT } from "../base/hkt";
+import { Mixin, type Input, type Return } from "../base/mixin";
+import { assert, assertType, type HidePrototype } from "../internal";
+ 
+type IterArgs<T extends readonly unknown[] = unknown[]> = [
+	element: T[number],
+	idx: number,
+	array: Readonly<T>,
+];
+ 
+type MaxDepth = 20;
+ 
+type Unordered<T extends readonly unknown[]> = T extends unknown[]
+	? T[number][]
+	: readonly T[number][];
+ 
+type Map<
+	T extends readonly unknown[],
+	U,
+	TOut extends U[] = [],
+> = TOut["length"] extends MaxDepth
+	? U[]
+	: T extends readonly [infer T, ...infer TRest extends unknown[]]
+		? Map<TRest, U, [...TOut, U]>
+		: T extends readonly []
+			? TOut
+			: U[];
+ 
+type Reverse<
+	T extends readonly unknown[],
+	TOut extends readonly T[number][] = [],
+> = TOut["length"] extends MaxDepth
+	? T[number][]
+	: T extends readonly [...infer TRest extends unknown[], infer T]
+		? Reverse<TRest, [...TOut, T]>
+		: T extends readonly []
+			? TOut
+			: T[number][];
+ 
+namespace At {
+	export type Dec<N extends number> = [
+		-1,
+		0,
+		1,
+		2,
+		3,
+		4,
+		5,
+		6,
+		7,
+		8,
+		9,
+		10,
+		11,
+		12,
+		13,
+		14,
+		15,
+		16,
+		17,
+		18,
+		19,
+		20,
+		21,
+	][N];
+ 
+	export type Select<
+		T extends readonly unknown[],
+		Skip extends number,
+	> = Skip extends 0
+		? T extends readonly [...unknown[], infer T]
+			? T
+			: undefined
+		: T extends readonly [...infer TRest, unknown]
+			? Select<TRest, Dec<Skip>>
+			: undefined;
+}
+ 
+type At<
+	T extends readonly unknown[],
+	K extends keyof T & number,
+> = number extends T["length"]
+	? T[K] | undefined
+	: `${K}` extends `-${infer N extends number}`
+		? At.Select<T, At.Dec<N>>
+		: T[K];
+ 
+type Repeat<
+	T extends readonly unknown[],
+	N extends number,
+	Acc extends null[] = [],
+	TOut extends readonly T[number][] = [],
+> = Acc["length"] extends N
+	? [...TOut, ...T]
+	: Acc["length"] extends MaxDepth
+		? T[number][]
+		: Repeat<T, N, [...Acc, null], [...TOut, ...T]>;
+ 
+namespace Flat {
+	type Inc<
+		N extends number,
+		Acc extends unknown[] = [],
+	> = Acc["length"] extends N
+		? [...Acc, null]["length"]
+		: Inc<N, [...Acc, null]>;
+ 
+	export type Leaves<
+		T,
+		Depth extends number = 0,
+	> = Depth extends MaxDepth
+		? T
+		: T extends readonly unknown[]
+			? Leaves<T[number], Inc<Depth>>
+			: T;
+ 
+	export type Rebuild<
+		T extends readonly unknown[],
+		Depth extends number = 0,
+	> = Depth extends MaxDepth
+		? T
+		: T extends readonly [infer H, ...infer R]
+			? H extends readonly unknown[]
+				? [...Rebuild<H, Inc<Depth>>, ...Rebuild<R, Depth>]
+				: [H, ...Rebuild<R, Depth>]
+			: [];
+}
+ 
+export type Flat<T extends readonly unknown[]> =
+	number extends T["length"] ? Flat.Leaves<T>[] : Flat.Rebuild<T>;
+ 
+function isArray(v: unknown): v is readonly unknown[] {
+	return typeof v === "object" && globalThis.Array.isArray(v);
+}
+ 
+export interface Array extends Mixin.HKT {
+	new: (t: HKT.T<this>) => Input<typeof t> extends infer T
+		? T extends readonly (infer Item)[]
+			? {
+					/**
+					 * Index the array using the specified zero-based index.
+					 * Negative indexes start at the end of the array.
+					 * @param index Zero-based index
+					 * @example
+					 * const array = ["first", "middle", "last"] as const;
+					 *
+					 * const first = $(array).at(0).value;
+					 * const middle = $(array).at(1).value;
+					 * const last = $(array).at(-1).value;
+					 *
+					 * expect(first).toBe("first");
+					 * expect(middle).toBe("middle");
+					 * expect(last).toBe("last");
+					 *
+					 * const oob = $(array).at(10).value;
+					 * expect(oob).toBe(undefined);
+					 * @from {@link Array `Array`}
+					 */
+					at: <const K extends keyof T & number>(
+						index: K,
+					) => Return<At<T, K>, typeof t>;
+					/**
+					 * Interospect each item of the array using the specified
+					 * callback
+					 * @param callback Callback taking item, index, and the array for each item.
+					 * @example
+					 * const array = [4, 5, 6] as const;
+					 *
+					 * let sum = 0;
+					 * const v = $(array).each((n) => { sum += n }).value;
+					 *
+					 * expect(sum).toBe(15);
+					 * expect(v).toMatchObject(array);
+					 * @from {@link Array `Array`}
+					 */
+					each: (
+						callback: (...args: IterArgs<T>) => void,
+					) => Return<T, typeof t>;
+					/**
+					 * Transform each item of the array using the specified callback
+					 * @param callback Transformative callback taking item, index, and array; returns the new item
+					 * @example
+					 * const chars = ["h", "e", "l", "l", "o"] as const;
+					 *
+					 * const v = $(chars).map(ch => ch.toUpperCase()).value;
+					 * expect(v).toMatchObject(["H", "E", "L", "L", "O"]);
+					 * @from {@link Array `Array`}
+					 */
+					map: <U>(
+						callback: (...args: IterArgs<T>) => U,
+					) => Return<Map<T, U>, typeof t>;
+					/**
+					 * Extend the array by repeating its current contents n times
+					 * @param count The number of repetitions to add
+					 * @example
+					 * const array = [1, 2];
+					 *
+					 * const v = $(array).extend(2).value;
+					 * expect(v).toMatchObject([1, 2, 1, 2, 1, 2]);
+					 * @from {@link Array `Array`}
+					 */
+					extend: <const N extends number>(
+						count: N,
+					) => Return<Repeat<T, N>, typeof t>;
+					/**
+					 * Filter the array to contain only items satisfying the
+					 * specified callback conditional
+					 * @param callback Callback getting item, index, and array for each item and determining if it should be filtered out or not
+					 * @example
+					 * const dirty = [-2, 4, 1, -5, -6] as const;
+					 *
+					 * const v = $(dirty).filter(v => v >= 0).value;
+					 * expect(v).toMatchObject([4, 1]);
+					 * @from {@link Array `Array`}
+					 */
+					filter: ((
+						callback: (...args: IterArgs<T>) => boolean,
+					) => Return<Unordered<T>, typeof t>) & {
+						/**
+						 * Filter the array to only contain items which are not
+						 * `null` or `undefined`.
+						 * @example
+						 * const array = new Array(5);
+						 *
+						 * array[0] = 1;
+						 * array[1] = 5;
+						 * array[2] = 2;
+						 *
+						 * const v = $(array).filter.some().value;
+						 * expect(v).toMatchObject([1, 5, 2]);
+						 * @from {@link Array `Array`}
+						 */
+						some: () => Return<
+							T extends unknown[]
+								? Exclude<
+										T[number],
+										null | undefined
+									>[]
+								: readonly Exclude<
+										T[number],
+										null | undefined
+									>[],
+							typeof t
+						>;
+					} & HidePrototype;
+					reduce: (<U>(
+						initial: U,
+						callback: (
+							value: U,
+							...args: IterArgs<T>
+						) => U,
+					) => Return<U, typeof t>) & {
+						right: <U>(
+							initial: U,
+							callback: (
+								value: U,
+								...args: IterArgs<T>
+							) => U,
+						) => Return<U, typeof t>;
+					} & HidePrototype;
+					length: () => Return<T["length"], typeof t>;
+					sort: ((
+						callback: (
+							a: Item,
+							b: Item,
+							arr: Readonly<T>,
+						) => number,
+					) => Return<Unordered<T>, typeof t>) &
+						([Item] extends [string]
+							? {
+									alpha: () => Return<
+										Unordered<T>,
+										typeof t
+									>;
+								}
+							: {
+									alpha: (
+										via: (v: Item) => string,
+									) => Return<
+										Unordered<T>,
+										typeof t
+									>;
+								}) &
+						([Item] extends [number]
+							? {
+									ascending: () => Return<
+										Unordered<T>,
+										typeof t
+									>;
+									descending: () => Return<
+										Unordered<T>,
+										typeof t
+									>;
+								}
+							: {
+									ascending: (
+										via: (v: Item) => number,
+									) => Return<
+										Unordered<T>,
+										typeof t
+									>;
+									descending: (
+										via: (v: Item) => number,
+									) => Return<
+										Unordered<T>,
+										typeof t
+									>;
+								}) &
+						HidePrototype;
+					reverse: () => Return<Reverse<T>, typeof t>;
+				}
+			: unknown
+		: never;
+}
+export const Array = Mixin<Array>((value, $, fluent) => {
+	if (!isArray(value)) return;
+ 
+	$.at = (index: number) => {
+		return fluent(value.at(index));
+	};
+ 
+	$.each = (callback: (...args: IterArgs) => void) => {
+		value.forEach(callback);
+		return fluent(value);
+	};
+ 
+	$.extend = (count: number) => {
+		assert(count >= 0);
+		Iif (count === 0) return fluent(value);
+		return fluent(
+			value.concat(...new globalThis.Array(count).fill(value)),
+		);
+	};
+ 
+	$.map = (callback: (...args: IterArgs) => unknown) => {
+		return fluent(value.map(callback));
+	};
+ 
+	$.filter = (callback: (...args: IterArgs) => boolean) => {
+		return fluent(value.filter(callback));
+	};
+	assertType<object>($.filter);
+	Object.assign($.filter, {
+		some: () => {
+			return fluent(
+				value.filter((v) => v !== null && v !== undefined),
+			);
+		},
+	});
+ 
+	$.reduce = (
+		initial: unknown,
+		callback: (value: unknown, ...args: IterArgs) => unknown,
+	) => {
+		return fluent(value.reduce(callback, initial));
+	};
+	assertType<object>($.reduce);
+	Object.assign($.reduce, {
+		right: (
+			initial: unknown,
+			callback: (value: unknown, ...args: IterArgs) => unknown,
+		) => {
+			return fluent(value.reduceRight(callback, initial));
+		},
+	});
+ 
+	$.length = () => {
+		return fluent(value.length);
+	};
+ 
+	$.sort = (
+		callback: (
+			a: unknown,
+			b: unknown,
+			arr: readonly unknown[],
+		) => number,
+	) => {
+		return fluent(
+			value.toSorted((a, b) => callback(a, b, value)),
+		);
+	};
+	assertType<object>($.sort);
+	Object.assign($.sort, {
+		alpha: (
+			via: (v: unknown) => string = (v) => (
+				assert(typeof v === "string"),
+				v
+			),
+		) => {
+			return fluent(
+				value.toSorted((a, b) =>
+					via(a)! < via(b)! ? -1 : 1,
+				),
+			);
+		},
+		ascending: (
+			via: (v: unknown) => number = (v) => (
+				assert(typeof v === "number"),
+				v
+			),
+		) => {
+			return fluent(value.toSorted((a, b) => via(a) - via(b)));
+		},
+		descending: (
+			via: (v: unknown) => number = (v) => (
+				assert(typeof v === "number"),
+				v
+			),
+		) => {
+			return fluent(value.toSorted((a, b) => via(b) - via(a)));
+		},
+	});
+ 
+	$.reverse = () => {
+		return fluent(value.toReversed());
+	};
+});
+ 
+if (import.meta.vitest) {
+	const { test, expect, vi } = import.meta.vitest;
+ 
+	const registry = [Array] as const;
+	const $ = makeFluent(registry);
+ 
+	test("at()", () => {
+		const arr = [1, 2, 3] as const;
+ 
+		expect($(arr).at(0).value).toBe(arr[0]);
+		expect($(arr).at(5).value).toBe(undefined);
+ 
+		expect($(arr).at(-1).value).toBe(arr[arr.length - 1]);
+		expect($(arr).at(-5).value).toBe(undefined);
+	});
+ 
+	test("each()", () => {
+		const arr = [5, 7, 1] as const;
+		const callback = vi.fn();
+ 
+		const result = $(arr).each(callback).value;
+ 
+		expect(result).toMatchObject(arr);
+ 
+		for (let i = 0; i < arr.length; i++)
+			expect(callback).toHaveBeenNthCalledWith(
+				i + 1,
+				arr[i],
+				i,
+				arr,
+			);
+	});
+ 
+	test("extend()", () => {
+		const arr = [1, 2] as const;
+ 
+		const copies = 2;
+		const expected: number[] = new globalThis.Array(copies + 1)
+			.fill(arr)
+			.flat(1);
+ 
+		expect($(arr).extend(copies).value).toMatchObject(expected);
+	});
+ 
+	test("map()", () => {
+		const arr = [1, 2, 3, 4, 5] as const;
+		const isEven = vi.fn((v: number) => v % 2 === 0);
+ 
+		expect($(arr).map(isEven).value).toMatchObject(
+			arr.map(isEven),
+		);
+ 
+		for (let i = 0; i < arr.length; i++)
+			expect(isEven).toHaveBeenNthCalledWith(
+				i + 1,
+				arr[i],
+				i,
+				arr,
+			);
+	});
+ 
+	test("filter()", () => {
+		const arr = [5, 4, 3, 2, 1] as const;
+		const isEven = vi.fn((v: number) => v % 2 === 0);
+ 
+		expect($(arr).filter(isEven).value).toMatchObject(
+			arr.filter(isEven),
+		);
+ 
+		for (let i = 0; i < arr.length; i++)
+			expect(isEven).toHaveBeenNthCalledWith(
+				i + 1,
+				arr[i],
+				i,
+				arr,
+			);
+ 
+		const dirty = [1, null, 6, undefined, 2] as const;
+		expect($(dirty).filter.some().value).toMatchObject(
+			dirty.filter((v) => v !== null && v !== undefined),
+		);
+	});
+ 
+	test("reduce()", () => {
+		const arr = [5, 5, 5, 5, 5] as const;
+ 
+		const callback = vi.fn((a: number, b: number) => a + b);
+		const expected = arr.reduce(callback, 0);
+ 
+		callback.mockClear();
+ 
+		expect($(arr).reduce(0, callback).value).toBe(expected);
+ 
+		let sum = 0;
+		for (let i = 0; i < arr.length; i++) {
+			expect(callback).toHaveBeenNthCalledWith(
+				i + 1,
+				sum,
+				arr[i],
+				i,
+				arr,
+			);
+			sum += arr[i]!;
+		}
+ 
+		callback.mockClear();
+ 
+		expect($(arr).reduce.right(0, callback).value).toBe(expected);
+ 
+		sum = 0;
+		for (let i = arr.length; i > 0; i--) {
+			expect(callback).toHaveBeenNthCalledWith(
+				arr.length - i + 1,
+				sum,
+				arr[i - 1],
+				i - 1,
+				arr,
+			);
+			sum += arr[i - 1]!;
+		}
+	});
+ 
+	test("length()", () => {
+		const arr = [3, 3, 3] as const;
+ 
+		expect($(arr).length().value).toBe(arr.length);
+	});
+ 
+	test("sort()", () => {
+		const numbers = [1, 5, 3, 4, 2] as const;
+ 
+		const callback = (a: number) => (a % 2 === 0 ? -1 : 1);
+ 
+		expect($(numbers).sort(callback).value).toMatchObject(
+			numbers.toSorted(callback),
+		);
+ 
+		expect($(numbers).sort.ascending().value).toMatchObject(
+			numbers.toSorted((a, b) => a - b),
+		);
+		expect($(numbers).sort.descending().value).toMatchObject(
+			numbers.toSorted((a, b) => b - a),
+		);
+ 
+		const boxed = [{ v: 3 }, { v: 2 }, { v: 1 }];
+		expect(
+			$(boxed).sort.ascending((v) => v.v).value,
+		).toMatchObject(boxed.toSorted((a, b) => a.v - b.v));
+		expect(
+			$(boxed).sort.descending((v) => v.v).value,
+		).toMatchObject(boxed.toSorted((a, b) => b.v - a.v));
+ 
+		const strings = [
+			"beta",
+			"alpha",
+			"bart",
+			"apple",
+			"charlie",
+		] as const;
+ 
+		expect($(strings).sort.alpha().value).toMatchObject(
+			strings.toSorted(),
+		);
+	});
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/src/mixin/awaited.ts.html b/coverage/src/mixin/awaited.ts.html new file mode 100644 index 0000000..69160fd --- /dev/null +++ b/coverage/src/mixin/awaited.ts.html @@ -0,0 +1,493 @@ + + + + + + Code coverage report for src/mixin/awaited.ts + + + + + + + + + +
+
+

All files / src/mixin awaited.ts

+
+ +
+ 93.1% + Statements + 27/29 +
+ + +
+ 100% + Branches + 7/7 +
+ + +
+ 81.81% + Functions + 9/11 +
+ + +
+ 92.3% + Lines + 24/26 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +5x +  +3x +1x +  +  +3x +  +  +1x +1x +  +  +1x +  +1x +  +2x +1x +1x +1x +  +  +1x +1x +1x +1x +  +  +  +  +1x +  +  +  +1x +  +  +1x +1x +  +  +  +  +  +1x +  +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { makeFluent } from "../base";
+import type { HKT } from "../base/hkt";
+import { Mixin, shim, type Input, type Return } from "../base/mixin";
+import { assert, assertType, never } from "../internal";
+import { Base } from "./base";
+ 
+interface AwaitedIdentitity extends HKT {
+	new: (t: HKT.T<this>) => Awaited<Promise<typeof t>>;
+}
+ 
+class Awaited<T extends Promise<unknown>> {
+	public value: T;
+	public constructor(value: T) {
+		this.value = value;
+	}
+ 
+	public get [shim]() {
+		return never as {
+			input: T extends Promise<infer U> ? U : never;
+			output: AwaitedIdentitity;
+			value: T;
+		};
+	}
+}
+ 
+export interface AsyncMixin extends Mixin.HKT {
+	new: (t: HKT.T<this>) => Input<typeof t> extends infer T
+		? T extends Promise<unknown>
+			? {
+					readonly awaited: Return<Awaited<T>, typeof t>;
+					then: <U>(
+						callback: (
+							value: T extends Promise<infer T>
+								? T
+								: never,
+						) => U,
+					) => Return<Promise<U>, typeof t>;
+				}
+			: unknown
+		: never;
+}
+ 
+interface BaseFluent<T> {
+	value: T;
+	[K: PropertyKey]: unknown;
+}
+ 
+export const AsyncMixin = Mixin<AsyncMixin>((value, $, fluent) => {
+	if (!(value instanceof Promise)) return;
+ 
+	$.then = (callback: (value: unknown) => unknown) => {
+		return fluent(value.then(callback));
+	};
+ 
+	Object.defineProperty($, "awaited", {
+		enumerable: true,
+		get() {
+			let v: Promise<BaseFluent<unknown>> = value.then((v) =>
+				fluent(v),
+			);
+ 
+			const path: PropertyKey[] = [];
+			// eslint-disable-next-line no-empty-pattern
+			const proxy = new Proxy((...[]: unknown[]) => proxy, {
+				get: (_, key) => {
+					if (key === "value")
+						return v.then((v) => v.value);
+					path.push(key);
+					return proxy;
+				},
+				apply: (target, thisArg, args: unknown[]) => {
+					v = v.then((v) => {
+						let obj: unknown = v;
+						for (const node of path) {
+							assert(
+								typeof obj === "object" &&
+									obj !== null &&
+									node in obj,
+							);
+							assertType<Record<PropertyKey, unknown>>(
+								obj,
+							);
+ 
+							obj = obj[node];
+						}
+ 
+						assert(typeof obj === "function");
+						assertType<
+							(
+								...args: unknown[]
+							) => BaseFluent<unknown>
+						>(obj);
+ 
+						return obj(...args);
+					});
+					return target.apply(thisArg, args);
+				},
+			});
+ 
+			return proxy;
+		},
+	});
+});
+ 
+if (import.meta.vitest) {
+	const { test, expect } = import.meta.vitest;
+ 
+	const registry = [Base, AsyncMixin] as const;
+	const $ = makeFluent(registry);
+ 
+	test(".awaited", async () => {
+		const value = 10;
+		const promise = new Promise<number>((r) => {
+			r(value);
+		});
+ 
+		const increment = (n: number) => n + 1;
+ 
+		const result = $(promise).awaited.transform(increment).value;
+ 
+		expect(await result).toBe(increment(value));
+	});
+ 
+	test("then()", async () => {
+		const value = 10;
+		const promise = new Promise<number>((r) => {
+			r(value);
+		});
+ 
+		const increment = (n: number) => n + 1;
+ 
+		const result = $(promise).then(increment).value;
+ 
+		expect(await result).toBe(increment(value));
+	});
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/src/mixin/base.ts.html b/coverage/src/mixin/base.ts.html new file mode 100644 index 0000000..7122f47 --- /dev/null +++ b/coverage/src/mixin/base.ts.html @@ -0,0 +1,331 @@ + + + + + + Code coverage report for src/mixin/base.ts + + + + + + + + + +
+
+

All files / src/mixin base.ts

+
+ +
+ 100% + Statements + 6/6 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 100% + Lines + 6/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +12x +1x +1x +  +  +12x +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { makeFluent } from "../base";
+import type { HKT } from "../base/hkt";
+import { Mixin, type Input, type Return } from "../base/mixin";
+ 
+export interface Base extends Mixin.HKT {
+	new: (t: HKT.T<this>) => Input<typeof t> extends infer T
+		? {
+				/**
+				 * Interospect value using the specified `callback` without
+				 * modifying the value.
+				 * @param callback The interospective callback
+				 * @see `transform` to modify
+				 * @example
+				 * let x;
+				 * const value = $(10).tap(v => { x = ++v }).value;
+				 *
+				 * expect(x).toBe(11);
+				 * expect(value).toBe(10);
+				 * @from {@link Base `Base`}
+				 */
+				tap(
+					callback: (value: Readonly<T>) => void,
+				): Return<T, typeof t>;
+				/**
+				 * Put value through or pipe value through the specified
+				 * `callback` using the outputted return value as a new value.
+				 * A.K.A., _transform_ the current value using a callback
+				 * @param callback The transformative callback
+				 * @example
+				 * const value = $("Hello")
+				 * 	.transform(v => v.toUpperCase())
+				 * 	.value;
+				 * expect(value).toBe("HELLO");
+				 * @from {@link Base `Base`}
+				 */
+				transform<U>(
+					callback: (value: T) => U,
+				): Return<U, typeof t>;
+			}
+		: never;
+}
+ 
+export const Base = Mixin<Base>((value, $, fluent) => {
+	$.tap = (callback: (value: unknown) => void) => {
+		callback(value);
+		return fluent(value);
+	};
+ 
+	$.transform = (callback: (value: unknown) => unknown) => {
+		return fluent(callback(value));
+	};
+});
+ 
+if (import.meta.vitest) {
+	const { test, expect } = import.meta.vitest;
+ 
+	const registry = [Base] as const;
+	const $ = makeFluent(registry);
+ 
+	test("tap()", () => {
+		const value = 5;
+		let out = 0;
+ 
+		const result = $(value).tap((v) => (out = v)).value;
+ 
+		expect(result).toBe(value);
+		expect(out).toBe(value);
+	});
+ 
+	test("transform()", () => {
+		const value = 5;
+		const increment = (v: number) => v + 1;
+ 
+		expect($(value).transform(increment).value).toBe(
+			increment(value),
+		);
+ 
+		expect(
+			$(value).transform(increment).transform(increment).value,
+		).toBe(increment(increment(value)));
+	});
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/src/mixin/index.html b/coverage/src/mixin/index.html new file mode 100644 index 0000000..635e30c --- /dev/null +++ b/coverage/src/mixin/index.html @@ -0,0 +1,161 @@ + + + + + + Code coverage report for src/mixin + + + + + + + + + +
+
+

All files src/mixin

+
+ +
+ 92.79% + Statements + 103/111 +
+ + +
+ 96.29% + Branches + 26/27 +
+ + +
+ 86.27% + Functions + 44/51 +
+ + +
+ 93.2% + Lines + 96/103 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
array.ts +
+
95.55%43/4592.3%12/1395.83%23/2497.56%40/41
awaited.ts +
+
93.1%27/29100%7/781.81%9/1192.3%24/26
base.ts +
+
100%6/6100%0/0100%3/3100%6/6
optional.ts +
+
87.09%27/31100%7/769.23%9/1386.66%26/30
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/src/mixin/optional.ts.html b/coverage/src/mixin/optional.ts.html new file mode 100644 index 0000000..39070bc --- /dev/null +++ b/coverage/src/mixin/optional.ts.html @@ -0,0 +1,880 @@ + + + + + + Code coverage report for src/mixin/optional.ts + + + + + + + + + +
+
+

All files / src/mixin optional.ts

+
+ +
+ 87.09% + Statements + 27/31 +
+ + +
+ 100% + Branches + 7/7 +
+ + +
+ 69.23% + Functions + 9/13 +
+ + +
+ 86.66% + Lines + 26/30 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +18x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +18x +5x +1x +  +  +5x +1x +  +5x +5x +  +1x +  +  +  +5x +  +  +5x +5x +  +  +  +  +  +13x +1x +  +  +13x +1x +  +  +13x +13x +  +1x +  +  +  +13x +  +  +13x +13x +  +  +  +  +  +  +18x +  +  +  +3x +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { makeFluent } from "../base";
+import type { HKT } from "../base/hkt";
+import {
+	Mixin,
+	type Input,
+	type Props,
+	type Return,
+} from "../base/mixin";
+import { assert, assertType, type HidePrototype } from "../internal";
+ 
+type NoneSentinel = null | undefined;
+type None<T> = Extract<T, NoneSentinel>;
+type Some<T> = Exclude<T, NoneSentinel>;
+ 
+function isNone<T>(v: T): v is None<T> {
+	return v === null || v === undefined;
+}
+ 
+interface Where<T, t extends Props> {
+	/**
+	 * Set a value to a fallback if it does not conform to some conditional
+	 * @param callback Callback returning a boolean, `false` sets the value to the fallback
+	 * @param fallback The fallback value, `null` by default
+	 * @example
+	 * const parse = () => { version: "1.2.4" } as unknown;
+	 *
+	 * const version = $(parse())
+	 *  .where(v => typeof v === 'object' &&
+	 *      v !== null &&
+	 *      'version' in v)
+	 *  .and(v => v.version)
+	 *  .or("1.0.0")
+	 *  .value;
+	 * expect(version).toBe("1.2.4");
+	 * @from {@link Optional `Optional`}
+	 */
+	where: <U = null>(
+		callback: (v: T) => boolean,
+		fallback?: U,
+	) => Return<T | U, t>;
+}
+ 
+export interface Optional extends Mixin.HKT {
+	new: (t: HKT.T<this>) => Input<typeof t> extends infer T
+		? None<T> extends never
+			? Where<T, typeof t>
+			: {
+					/**
+					 * Transform a value via callback if it is not `null` or
+					 * `undefined`
+					 *
+					 * If the value is equal to `null` or `undefined`, it
+					 * remains unchanged and the callback is not called.
+					 * @param callback Function for a non-null value
+					 * @example
+					 * const none = () => null as number | null;
+					 * const some = () => 10 as number | null;
+					 *
+					 * const callback = (v: number) => v + 5;
+					 *
+					 * const a = $(none()).and(callback).value;
+					 * expect(a).toBe(null)
+					 *
+					 * const b = $(some()).and(callback).value;
+					 * expect(b).toBe(15);
+					 * @from {@link Optional `Optional`}
+					 */
+					and: <U>(
+						callback: (v: Some<T>) => U,
+					) => Return<None<T> | U, typeof t>;
+					/**
+					 * Set value to a fallback if it is `null` or `undefined`.
+					 * @param fallback The fallback value to use. To defer
+					 * computing this fallback value, you can use `.or.else()`
+					 * @example
+					 * const none = () => null as number | null;
+					 * const some = () => 10 as number | null;
+					 *
+					 * const fallback = -1;
+					 *
+					 * const a = $(none()).or(fallback).value;
+					 * expect(a).toBe(fallback);
+					 *
+					 * const b = $(some()).or(fallback).value;
+					 * expect(b).toBe(10);
+					 * @from {@link Optional `Optional`}
+					 */
+					or: (<U>(
+						fallback: U,
+					) => Return<Some<T> | U, typeof t>) & {
+						/**
+						 * Set value to the result of `callback` if it is `null`
+						 * or `undefined`. Unlike the normal `.or()`, this method
+						 * only computes the fallback if the value is `null` or
+						 * `undefined`
+						 * @param callback
+						 * @example
+						 * const none = () => null as number | null;
+						 * const some = () => 8 as number | null;
+						 *
+						 * const fallback = () => 22; // mocked
+						 *
+						 * const a = $(none()).or.else(fallback).value;
+						 * expect(a).toBe(22);
+						 *
+						 * const b = $(some()).or.else(fallback).value;
+						 * expect(b).toBe(8);
+						 *
+						 * expect(fallback).toHaveBeenCalledOnce()
+						 * @from {@link Optional `Optional`}
+						 */
+						else: <U>(
+							callback: (v: None<T>) => U,
+						) => Return<Some<T> | U, typeof t>;
+					} & HidePrototype;
+					/**
+					 * Assert that value is not `null` or `undefined`
+					 * @param msg Reasoning to attach to the `AssertionError`
+					 * @example
+					 * const array = [1, 2, 3];
+					 * const element: number | undefined = array[1];
+					 *
+					 * const v = $(element).assert("index within bounds").value;
+					 * expect(v).toBe(2);
+					 *
+					 * expect(() => {
+					 *  $(array[6]).assert()
+					 * }).toThrow()
+					 * @see `.assert.none()` for the inverse assertion
+					 * @from {@link Optional `Optional`}
+					 */
+					assert: ((
+						msg?: string,
+					) => Return<Some<T>, typeof t>) & {
+						/**
+						 * Assert that the value is either `null` or `undefined`
+						 * @param msg Reasoning to attach to the `AssertionError`
+						 * @example
+						 * const array = [1, 2, 3];
+						 * const element: number | undefined = array[5];
+						 *
+						 * const v = $(element).assert.none("index out of bounds").value;
+						 * expect(v).toBe(undefined);
+						 *
+						 * expect(() => {
+						 *  $(array[1]).assert.none()
+						 * }).toThrow()
+						 */
+						none: (
+							msg?: string,
+						) => Return<None<T>, typeof t>;
+					} & HidePrototype;
+				} & Where<T, typeof t>
+		: never;
+}
+ 
+export const Optional = Mixin<Optional>((value, $, fluent) => {
+	if (isNone(value)) {
+		$.and = () => {
+			return fluent(value);
+		};
+ 
+		$.or = (fallback: unknown) => {
+			return fluent(fallback);
+		};
+		assertType<object>($.or);
+		Object.assign($.or, {
+			else: (callback: (v: unknown) => unknown) => {
+				return fluent(callback(value));
+			},
+		});
+ 
+		$.assert = () => {
+			return fluent(value);
+		};
+		assertType<object>($.assert);
+		Object.assign($.assert, {
+			none: (msg?: string) => {
+				assert(false, msg);
+			},
+		});
+	} else {
+		$.and = (callback: (v: unknown) => unknown) => {
+			return fluent(callback(value));
+		};
+ 
+		$.or = () => {
+			return fluent(value);
+		};
+ 
+		assertType<object>($.or);
+		Object.assign($.or, {
+			else: () => {
+				return fluent(value);
+			},
+		});
+ 
+		$.assert = (msg?: string) => {
+			assert(false, msg);
+		};
+		assertType<object>($.assert);
+		Object.assign($.assert, {
+			none: (msg?: string) => {
+				return fluent(value);
+			},
+		});
+	}
+ 
+	$.where = (
+		callback: (v: unknown) => boolean,
+		fallback = null,
+	) => {
+		if (callback(value)) return fluent(value);
+		return fluent(fallback);
+	};
+});
+ 
+if (import.meta.vitest) {
+	const { test, expect, vi } = import.meta.vitest;
+ 
+	const registry = [Optional] as const;
+	const $ = makeFluent(registry);
+ 
+	type Value = number | null;
+ 
+	test("and()", () => {
+		const value = 10;
+		const some = value as Value;
+		const none = null as Value;
+ 
+		const callback = (v: number) => v + 5;
+ 
+		expect($(some).and(callback).value).toBe(callback(value));
+		expect($(none).and(callback).value).toBe(null);
+	});
+ 
+	test("or()", () => {
+		const some = 10 as Value;
+		const none = null as Value;
+ 
+		const fallback = 15 as const;
+ 
+		expect($(some).or(fallback).value).toBe(some);
+		expect($(none).or(fallback).value).toBe(fallback);
+ 
+		const callback = vi.fn(() => fallback);
+ 
+		expect($(some).or.else(callback).value).toBe(some);
+		expect($(none).or.else(callback).value).toBe(fallback);
+ 
+		expect(callback).toHaveBeenCalledOnce();
+	});
+ 
+	test("where()", () => {
+		const even = 4;
+		const odd = 7;
+ 
+		const isEven = (v: number) => v % 2 === 0;
+ 
+		expect($(even).where(isEven).value).toBe(even);
+		expect($(odd).where(isEven).value).toBe(null);
+ 
+		expect($(odd).where(isEven, -1).value).toBe(-1);
+	});
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/package.json b/package.json index 18e436b..9cce1fa 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "test": "vitest --run --reporter=tree", + "test": "vitest --run --reporter=tree --coverage", "build": "tsc -b && esbuild --minify --bundle src/index.ts --outdir=dist --define:import.meta.vitest=undefined", "fmt": "prettier --write .", "lint": "eslint .", @@ -14,6 +14,7 @@ "@eslint/js": "^10.0.1", "@types/node": "^24.12.3", "@typescript-eslint/types": "^8.61.1", + "@vitest/coverage-v8": "4.1.9", "esbuild": "^0.28.1", "eslint": "^10.3.0", "jiti": "^2.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2429a89..c710453 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@typescript-eslint/types': specifier: ^8.61.1 version: 8.61.1 + '@vitest/coverage-v8': + specifier: 4.1.9 + version: 4.1.9(vitest@4.1.9) esbuild: specifier: ^0.28.1 version: 0.28.1 @@ -40,7 +43,7 @@ importers: version: 8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) vitest: specifier: ^4.1.9 - version: 4.1.9(@types/node@24.13.2)(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)) + version: 4.1.9(@types/node@24.13.2)(@vitest/coverage-v8@4.1.9)(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)) packages: @@ -61,6 +64,10 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@dependents/detective-less@5.0.3': resolution: {integrity: sha512-v6oD9Ukp+N7V4n6p5I/+mM5fIohSfkrDSGlFm5w/pYmchvbk+sMIHsLxrFJ5Lnujewj1BzWL0K84d88lwZAMQA==} engines: {node: '>=18'} @@ -293,9 +300,16 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@napi-rs/wasm-runtime@1.1.5': resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} peerDependencies: @@ -496,6 +510,15 @@ packages: resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitest/coverage-v8@4.1.9': + resolution: {integrity: sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==} + peerDependencies: + '@vitest/browser': 4.1.9 + vitest: 4.1.9 + peerDependenciesMeta: + '@vitest/browser': + optional: true + '@vitest/expect@4.1.9': resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} @@ -575,6 +598,9 @@ packages: resolution: {integrity: sha512-6KuK/7nZ/2Qh7sGuVEiwxjCxzTY2Pdb5mTo5z1e6/J8BA0tvjR7G8vQJKrQMTqwmnA3UPEyKIFX4YUS1DO1Hvw==} engines: {node: '>=18'} + ast-v8-to-istanbul@1.0.4: + resolution: {integrity: sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==} + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -867,6 +893,9 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -923,10 +952,25 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -1039,6 +1083,13 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -1464,6 +1515,8 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@bcoe/v8-coverage@1.0.2': {} + '@dependents/detective-less@5.0.3': dependencies: gonzales-pe: 4.3.0 @@ -1615,8 +1668,15 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 @@ -1835,6 +1895,20 @@ snapshots: '@typescript-eslint/types': 8.61.1 eslint-visitor-keys: 5.0.1 + '@vitest/coverage-v8@4.1.9(vitest@4.1.9)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.9 + ast-v8-to-istanbul: 1.0.4 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.3 + obug: 2.1.3 + std-env: 4.1.0 + tinyrainbow: 3.1.0 + vitest: 4.1.9(@types/node@24.13.2)(@vitest/coverage-v8@4.1.9)(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)) + '@vitest/expect@4.1.9': dependencies: '@standard-schema/spec': 1.1.0 @@ -1935,6 +2009,12 @@ snapshots: ast-module-types@6.0.2: {} + ast-v8-to-istanbul@1.0.4: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + balanced-match@4.0.4: {} base64-js@1.5.1: {} @@ -2263,6 +2343,8 @@ snapshots: dependencies: function-bind: 1.1.2 + html-escaper@2.0.2: {} + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -2297,8 +2379,23 @@ snapshots: isexe@2.0.0: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + jiti@2.7.0: {} + js-tokens@10.0.0: {} + json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} @@ -2397,6 +2494,16 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.8.4 + mimic-fn@2.1.0: {} minimatch@10.2.5: @@ -2714,7 +2821,7 @@ snapshots: fsevents: 2.3.3 jiti: 2.7.0 - vitest@4.1.9(@types/node@24.13.2)(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)): + vitest@4.1.9(@types/node@24.13.2)(@vitest/coverage-v8@4.1.9)(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)): dependencies: '@vitest/expect': 4.1.9 '@vitest/mocker': 4.1.9(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)) @@ -2738,6 +2845,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.13.2 + '@vitest/coverage-v8': 4.1.9(vitest@4.1.9) transitivePeerDependencies: - msw diff --git a/src/mixin/array.ts b/src/mixin/array.ts index 381f4fc..d1cd3ff 100644 --- a/src/mixin/array.ts +++ b/src/mixin/array.ts @@ -80,9 +80,22 @@ namespace At { type At< T extends readonly unknown[], K extends keyof T & number, -> = `${K}` extends `-${infer N extends number}` - ? At.Select> - : T[K]; +> = number extends T["length"] + ? T[K] | undefined + : `${K}` extends `-${infer N extends number}` + ? At.Select> + : T[K]; + +type Repeat< + T extends readonly unknown[], + N extends number, + Acc extends null[] = [], + TOut extends readonly T[number][] = [], +> = Acc["length"] extends N + ? [...TOut, ...T] + : Acc["length"] extends MaxDepth + ? T[number][] + : Repeat; namespace Flat { type Inc< @@ -124,18 +137,112 @@ export interface Array extends Mixin.HKT { new: (t: HKT.T) => Input extends infer T ? T extends readonly (infer Item)[] ? { + /** + * Index the array using the specified zero-based index. + * Negative indexes start at the end of the array. + * @param index Zero-based index + * @example + * const array = ["first", "middle", "last"] as const; + * + * const first = $(array).at(0).value; + * const middle = $(array).at(1).value; + * const last = $(array).at(-1).value; + * + * expect(first).toBe("first"); + * expect(middle).toBe("middle"); + * expect(last).toBe("last"); + * + * const oob = $(array).at(10).value; + * expect(oob).toBe(undefined); + * @from {@link Array `Array`} + */ at: ( index: K, ) => Return, typeof t>; + /** + * Interospect each item of the array using the specified + * callback + * @param callback Callback taking item, index, and the array for each item. + * @example + * const array = [4, 5, 6] as const; + * + * let sum = 0; + * const v = $(array).each((n) => { sum += n }).value; + * + * expect(sum).toBe(15); + * expect(v).toMatchObject(array); + * @from {@link Array `Array`} + */ each: ( callback: (...args: IterArgs) => void, ) => Return; + /** + * Transform each item of the array using the specified callback + * @param callback Transformative callback taking item, index, and array; returns the new item + * @example + * const chars = ["h", "e", "l", "l", "o"] as const; + * + * const v = $(chars).map(ch => ch.toUpperCase()).value; + * expect(v).toMatchObject(["H", "E", "L", "L", "O"]); + * @from {@link Array `Array`} + */ map: ( callback: (...args: IterArgs) => U, ) => Return, typeof t>; - filter: ( + /** + * Extend the array by repeating its current contents n times + * @param count The number of repetitions to add + * @example + * const array = [1, 2]; + * + * const v = $(array).extend(2).value; + * expect(v).toMatchObject([1, 2, 1, 2, 1, 2]); + * @from {@link Array `Array`} + */ + extend: ( + count: N, + ) => Return, typeof t>; + /** + * Filter the array to contain only items satisfying the + * specified callback conditional + * @param callback Callback getting item, index, and array for each item and determining if it should be filtered out or not + * @example + * const dirty = [-2, 4, 1, -5, -6] as const; + * + * const v = $(dirty).filter(v => v >= 0).value; + * expect(v).toMatchObject([4, 1]); + * @from {@link Array `Array`} + */ + filter: (( callback: (...args: IterArgs) => boolean, - ) => Return, typeof t>; + ) => Return, typeof t>) & { + /** + * Filter the array to only contain items which are not + * `null` or `undefined`. + * @example + * const array = new Array(5); + * + * array[0] = 1; + * array[1] = 5; + * array[2] = 2; + * + * const v = $(array).filter.some().value; + * expect(v).toMatchObject([1, 5, 2]); + * @from {@link Array `Array`} + */ + some: () => Return< + T extends unknown[] + ? Exclude< + T[number], + null | undefined + >[] + : readonly Exclude< + T[number], + null | undefined + >[], + typeof t + >; + } & HidePrototype; reduce: (( initial: U, callback: ( @@ -217,6 +324,14 @@ export const Array = Mixin((value, $, fluent) => { return fluent(value); }; + $.extend = (count: number) => { + assert(count >= 0); + if (count === 0) return fluent(value); + return fluent( + value.concat(...new globalThis.Array(count).fill(value)), + ); + }; + $.map = (callback: (...args: IterArgs) => unknown) => { return fluent(value.map(callback)); }; @@ -224,6 +339,14 @@ export const Array = Mixin((value, $, fluent) => { $.filter = (callback: (...args: IterArgs) => boolean) => { return fluent(value.filter(callback)); }; + assertType($.filter); + Object.assign($.filter, { + some: () => { + return fluent( + value.filter((v) => v !== null && v !== undefined), + ); + }, + }); $.reduce = ( initial: unknown, @@ -326,6 +449,17 @@ if (import.meta.vitest) { ); }); + test("extend()", () => { + const arr = [1, 2] as const; + + const copies = 2; + const expected: number[] = new globalThis.Array(copies + 1) + .fill(arr) + .flat(1); + + expect($(arr).extend(copies).value).toMatchObject(expected); + }); + test("map()", () => { const arr = [1, 2, 3, 4, 5] as const; const isEven = vi.fn((v: number) => v % 2 === 0); @@ -358,6 +492,11 @@ if (import.meta.vitest) { i, arr, ); + + const dirty = [1, null, 6, undefined, 2] as const; + expect($(dirty).filter.some().value).toMatchObject( + dirty.filter((v) => v !== null && v !== undefined), + ); }); test("reduce()", () => { diff --git a/src/mixin/base.ts b/src/mixin/base.ts index 4ac329f..4b22c31 100644 --- a/src/mixin/base.ts +++ b/src/mixin/base.ts @@ -5,9 +5,34 @@ import { Mixin, type Input, type Return } from "../base/mixin"; export interface Base extends Mixin.HKT { new: (t: HKT.T) => Input extends infer T ? { + /** + * Interospect value using the specified `callback` without + * modifying the value. + * @param callback The interospective callback + * @see `transform` to modify + * @example + * let x; + * const value = $(10).tap(v => { x = ++v }).value; + * + * expect(x).toBe(11); + * expect(value).toBe(10); + * @from {@link Base `Base`} + */ tap( - callback: (value: T) => void, + callback: (value: Readonly) => void, ): Return; + /** + * Put value through or pipe value through the specified + * `callback` using the outputted return value as a new value. + * A.K.A., _transform_ the current value using a callback + * @param callback The transformative callback + * @example + * const value = $("Hello") + * .transform(v => v.toUpperCase()) + * .value; + * expect(value).toBe("HELLO"); + * @from {@link Base `Base`} + */ transform( callback: (value: T) => U, ): Return; diff --git a/src/mixin/math.ts b/src/mixin/math.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/mixin/optional.ts b/src/mixin/optional.ts index 64e9ad9..76bb3f2 100644 --- a/src/mixin/optional.ts +++ b/src/mixin/optional.ts @@ -6,7 +6,7 @@ import { type Props, type Return, } from "../base/mixin"; -import { assertType, type HidePrototype } from "../internal"; +import { assert, assertType, type HidePrototype } from "../internal"; type NoneSentinel = null | undefined; type None = Extract; @@ -17,6 +17,23 @@ function isNone(v: T): v is None { } interface Where { + /** + * Set a value to a fallback if it does not conform to some conditional + * @param callback Callback returning a boolean, `false` sets the value to the fallback + * @param fallback The fallback value, `null` by default + * @example + * const parse = () => { version: "1.2.4" } as unknown; + * + * const version = $(parse()) + * .where(v => typeof v === 'object' && + * v !== null && + * 'version' in v) + * .and(v => v.version) + * .or("1.0.0") + * .value; + * expect(version).toBe("1.2.4"); + * @from {@link Optional `Optional`} + */ where: ( callback: (v: T) => boolean, fallback?: U, @@ -28,16 +45,111 @@ export interface Optional extends Mixin.HKT { ? None extends never ? Where : { + /** + * Transform a value via callback if it is not `null` or + * `undefined` + * + * If the value is equal to `null` or `undefined`, it + * remains unchanged and the callback is not called. + * @param callback Function for a non-null value + * @example + * const none = () => null as number | null; + * const some = () => 10 as number | null; + * + * const callback = (v: number) => v + 5; + * + * const a = $(none()).and(callback).value; + * expect(a).toBe(null) + * + * const b = $(some()).and(callback).value; + * expect(b).toBe(15); + * @from {@link Optional `Optional`} + */ and: ( callback: (v: Some) => U, ) => Return | U, typeof t>; + /** + * Set value to a fallback if it is `null` or `undefined`. + * @param fallback The fallback value to use. To defer + * computing this fallback value, you can use `.or.else()` + * @example + * const none = () => null as number | null; + * const some = () => 10 as number | null; + * + * const fallback = -1; + * + * const a = $(none()).or(fallback).value; + * expect(a).toBe(fallback); + * + * const b = $(some()).or(fallback).value; + * expect(b).toBe(10); + * @from {@link Optional `Optional`} + */ or: (( fallback: U, ) => Return | U, typeof t>) & { + /** + * Set value to the result of `callback` if it is `null` + * or `undefined`. Unlike the normal `.or()`, this method + * only computes the fallback if the value is `null` or + * `undefined` + * @param callback + * @example + * const none = () => null as number | null; + * const some = () => 8 as number | null; + * + * const fallback = () => 22; // mocked + * + * const a = $(none()).or.else(fallback).value; + * expect(a).toBe(22); + * + * const b = $(some()).or.else(fallback).value; + * expect(b).toBe(8); + * + * expect(fallback).toHaveBeenCalledOnce() + * @from {@link Optional `Optional`} + */ else: ( callback: (v: None) => U, ) => Return | U, typeof t>; } & HidePrototype; + /** + * Assert that value is not `null` or `undefined` + * @param msg Reasoning to attach to the `AssertionError` + * @example + * const array = [1, 2, 3]; + * const element: number | undefined = array[1]; + * + * const v = $(element).assert("index within bounds").value; + * expect(v).toBe(2); + * + * expect(() => { + * $(array[6]).assert() + * }).toThrow() + * @see `.assert.none()` for the inverse assertion + * @from {@link Optional `Optional`} + */ + assert: (( + msg?: string, + ) => Return, typeof t>) & { + /** + * Assert that the value is either `null` or `undefined` + * @param msg Reasoning to attach to the `AssertionError` + * @example + * const array = [1, 2, 3]; + * const element: number | undefined = array[5]; + * + * const v = $(element).assert.none("index out of bounds").value; + * expect(v).toBe(undefined); + * + * expect(() => { + * $(array[1]).assert.none() + * }).toThrow() + */ + none: ( + msg?: string, + ) => Return, typeof t>; + } & HidePrototype; } & Where : never; } @@ -51,13 +163,22 @@ export const Optional = Mixin((value, $, fluent) => { $.or = (fallback: unknown) => { return fluent(fallback); }; - assertType($.or); Object.assign($.or, { else: (callback: (v: unknown) => unknown) => { return fluent(callback(value)); }, }); + + $.assert = () => { + return fluent(value); + }; + assertType($.assert); + Object.assign($.assert, { + none: (msg?: string) => { + assert(false, msg); + }, + }); } else { $.and = (callback: (v: unknown) => unknown) => { return fluent(callback(value)); @@ -73,6 +194,16 @@ export const Optional = Mixin((value, $, fluent) => { return fluent(value); }, }); + + $.assert = (msg?: string) => { + assert(false, msg); + }; + assertType($.assert); + Object.assign($.assert, { + none: (msg?: string) => { + return fluent(value); + }, + }); } $.where = ( @@ -85,7 +216,7 @@ export const Optional = Mixin((value, $, fluent) => { }); if (import.meta.vitest) { - const { test, expect } = import.meta.vitest; + const { test, expect, vi } = import.meta.vitest; const registry = [Optional] as const; const $ = makeFluent(registry); @@ -112,10 +243,12 @@ if (import.meta.vitest) { expect($(some).or(fallback).value).toBe(some); expect($(none).or(fallback).value).toBe(fallback); - const callback = () => fallback; + const callback = vi.fn(() => fallback); expect($(some).or.else(callback).value).toBe(some); expect($(none).or.else(callback).value).toBe(fallback); + + expect(callback).toHaveBeenCalledOnce(); }); test("where()", () => {