mona-dish
v0.50.0-beta.8
Published
TypeScript/Javascript set of functional pattern inspired helpers, which will ease your programming live
Readme
Mona-dish
A set of functional programming "inspired" helpers
What is it?
This project is a set of small helpers which utilize mostly monad and monad like patterns to cut down on code for essential tasks.
For now, it is only a small set of Helpers consisting of following items:
- Monad ... an implementation of a Monad
- Optional ... a class which is derived from Javas optional but also encapsulates elvis operator like accessors to cut down on code
- ValueEmbedder ... if you ever need something like optional but want to write the value as well this might be what you are looking for
- Promise ... a promise shim implementation for older browsers (newer ones have Promised baked in), will be deprecated soon, there are better options
- CancellablePromise ... a promise with cancel functionality
- Configuration ... an Optional utilizing wrapper over json configurations which allow both read and write access and elvis like access to the data stored in the config
- Streams ... a typescript based implementation of early and lazily evaluating streams
- DomQuery ... a jquery like functional query and dom manipulation engine based on querySelectorAll, also support streams and shadow dom trees
- XmlQuery ... a jquery like XML document query selection and manipulation engine ... also supports streams
- Messaging ... a messaging bus which can break page isolation levels to allow communication between iframes/popups/shadow dom/dom, exposed via
mona-dish/messaging - RxJS ... RxJS bindings for the optional messaging module
Breaking change: messaging moved to an explicit entry point
The default mona-dish package entry now exposes the core API only and does not load the RxJS-backed messaging module.
Projects using messaging must import it from mona-dish/messaging:
import {Broker, BroadcastChannelBroker, Message} from "mona-dish/messaging";rxjs remains a consumer-provided external dependency. Install/provide it in applications which use mona-dish/messaging.
Implementation languages
Everything is implemented in typescript and can be used straight from the source directories "src/main/typescript".
However, also javascript transpilations for various packaging systems and ecmascript levels are in place as well hosted under "dist".
If you want a cleaner cut between your own typescript sources, and the mona-dish sources there is a d.ts file also, hosted under "dist".
building
For building the project you need following
- npm
- webpack
once this is done you can build it by calling ./init.sh one time to install all the needed build dependencies, after that calling /build.sh rebuilds the entire project.
You also can run mocha based unit tests on the project by calling ./test.sh
(note this is for unixoid systems, windows command files will be added soon, in the meanwhile simply check the command sequences in the sh files for building and testing)
Usage
Optional
The enhanced documentation for optional can be found here:
For a non sideffect free implementation, you can use:
ValueEmbedder
Optional is a purely readonly construct, now for sideffect free-ness, having only readonly operations is fine. However, in iterative systems we often deal with states. To get the conciseness of Optional also for writeable states there is a class available which is inherited from optional and hence shares the same functionality.
ValueEmbedder
Configuration
Promise and CancellablePromise
Promise is just a lightweight shim of the Promise API including finally. Cancellable promise adds on top of that by allowing to cancel (aka never hit the then/cancel phase)
Stream
The idea is to have streams like java does for arrays. (forEach, filter etc...)
Details
The streams are heavily influenced by the java streams. Currently, two type of streams are implemented
- Early streams (Streams)
The default (working already) a simple implementation of early evaluating streams
- Lazy Streams (LazyStreams) Lazily evaluating streams, aka elements are processed at the latest possible stage, this is the default in Java. The advantage of those is you basically can process endless data without any impact on ram. Hence there is a set of Data Providers implemented and a general DataProvider interface available.
Usage
beforeEach(function () {
this.probe = [1, 2, 3, 4, 5];
});
it("must have a correct first last lazy", function () {
let stream = LazyStream.of<number>(...this.probe);
let first = LazyStream.of<number>(...this.probe).filter((data) => data != 5).onElem((data) => {
data;
}).first().value;
let last = Stream.of<number>(...this.probe).filter((data) => data != 5).onElem((data) => {
data;
}).last().value;
expect(first).to.eq(1);
expect(last).to.eq(4);
});Or in conjunction with DomQuery
let searchRoot = DomQuery.querySelectorAll("input[type='hidden']")
let formWindowId: Optional<string> = searchRoot
.stream
.map<string>(getValue)
.reduce(doubleCheck, INIT);
let otherStream = LazyStream.ofDataSource(searchRoot);See StreamTest.spec.ts in the "test sources" directory for additional examples
DomQuery
A JQuery like interface for standard dom queries, also supports Streams and Shadow doms. Further Info can be found here
DomQuery ... a jquery like functional query and dom manipulation engine based on querySelectorAll, also support streams and shadow dom trees
XmlQuery
similar to DomQuery but atm without a full query engine behind it, the reason for that is, that the browsers do not have a universal query engine, yet. Also, I tried to avoid third party dependencies. You, also, will get many other benefits similar to DomQuery by using XmlQuery
Messaging
A messaging bus ... for documentation follow this link:
Messaging is not exported by the default mona-dish package entry anymore. Use:
import {Broker, BroadcastChannelBroker, Message} from "mona-dish/messaging";Extended Documentation
I am going to provide extended documentation on the various aspects of mona-dish in following subpages
Examples
Various usage examples can be found in the tests:
Changelog
(Starting with version 0.18)
...
Version 0.18
Adding Extended Array, to provide shim like functionality without shims (adds several functions to the standard array older browsers do not support, but does not hook itself in like a shim)
Version 0.19
- New messaging submodule
- Improved documentation
- DomQuery: Shadow Dom support
Version 0.19.1 - 0.19.22
- ongoing works in the messaging area (not fully finalized)
- minor api change, channel now parameter in broker.broadcast instead of part of the message.
- setting the code baseline of the compiled code to es5
- Messaging will be finalized in version 0.20.x
- Minor compiler issues fixed in the "typings" area which could cause typescript warnings
- Bidirectional messaging which allows answer/response patterns
- Re-enabling IE11 compatibility
- basing the messaging on top of the broadcast channel as universal base, the messaging is basically a broadcast channel++ with additional functionality
Version 0.20.++
Rxjs connectivity, rxjs is a more popular framework than mona-dish but both have a heavy functional overlap. It makes sense to open mona-dish for rjxs in both directions to be able to combine both frameworks easily
RXJS forward connectivity enabled via iterable implementation! you can basically now use streams as iterables
20.3 Adding basic rxjs support for streams
20.4-20.5 Adding rxjs connectivity apis for the messages, brokerchannels now can be exported as Subjects
0.20.7 Adding encryption extension points to the messaging api
0.20.8 extension refinements tests for the extensions documentation updates
Version 0.21.++
- 0.21.0 adding a mutation observer
Version 0.22 ++
- Api change, the Promise has been removed from the index.js and resides in its own PromiseShim.js file. The reason was UMD builds did not check for an existing Promise before installing it. It made sense to decouple it anyway, literally all modern browsers do not need it anymore. CancellablePromise is still enabled.
- Added convenience methods for easier access to value (via val) and innerHTML,
- mutation observer fixes
- improved nonce handling
- improved rxjs connectivity
- documentation updates
- changing default package type from commonjs to umd, to improve ide handling
- adding a sticky eval to DomQuery to keep the evaled elements in the head
- adding a sticky eval handling to run scripts as well
Version 0.23+
- (minor version change due to an api change which has been reverted)
- fixing the script loading, loading order now in a series of scripts is in sync with mixed src and embedded scripts (no api change)
Version 0.24 ++
- Optional typed and semi typed configs which allow fixed keys and subtrees of keys which are not predefined
Version 0.25 ++
- Fixing of bugs in the stream area
- API change for the lookAhead (might break old code using it)
- Added Config Collector similar to Assoc Array Collectors
This is from now on a development branch, to finally split between unstable and stable
0.25.20
Breaking change:
To get reduced coupling between modules (I want to make the usage of Config optional) DomQuery does not use Config anymore but relies on standard associative arrays:
- before: encodeFormElement(toMerge: Config): Config;
- new: encodeFormElement(toMerge: {[key: string]: any}): {[key: string]: any};
Config now is not a dependency of DomQuery anymore!
A new module AssocArray provides helper functions to provide a similar functionality as Config
0.25.26
Another minor breaking change.
0.25.26 reorganizes the internal data structure of config to allow easier access and a direct mapping of associative arrays and arrays. (Allows for easier debugging internal data structures and a small speed bump) due to this change following construct is not possible anymore
let conf = new Config({}); conf.assign("[5]","[6]","key").value = "new value";
This goes hand in hand with the newly introduced low level associative array api you have to change the code accordingly let conf = new Config([]); conf.assign("[5]","[6]","key").value = "new value";
So always when an array is referenced from root, the root element must be an array otherwise an exception is thrown (the same goes for existing subelemements)
(the same goes implicity for associative arrays in the other direction, arrays cannot have key references anymore with assignments)
Version 0.26 ++
- Fixing of bugs in the stream area
- API change for the lookAhead (might break old code using it)
- Added Config Collector similar to Assoc Array Collectors
This is from now on a stable branch (and all even numbered ones will be, with every odd branch there will be a higher even numbered one release to avoid accidental downloads)
Version 0.28++
Important API chnage
- Decoupling of Stream and DomQuery
In order to reduce the possible include size, there now is a stronger decoupling between streams and DomQuery. You now can use DomQuery without linking the Streams. In order to do so, the API has slightly changed. DomQuery.streams now is gone To generate a Stream of a DomQuery object you can use Stream.ofDomQuery and LazyStream.ofDomQuery
The reason was that Streams have a significant code overhead and with ES2019 the native api has somewhat reached the status of good enough with the inclusion of map. (internally we provide a non, bleeding shim for that functionality)
If you include Streams the backwards compatibility is restored
- Decoupling of DomQuery and Config
Additional api change, in order do decouple DomQuery and Config encodeSubmittableFields now returns an associative array instead of a Config. You can convert it back to an associative array simply by using
new Config(dq.encodeSubmittableFields())
Other changes 0.28++
Es2019 Array as non-intrusive Es2019 array shim (providing filter and flapMap to browsers which do not support it)
New helper module providing functions similar to what config does to plain associative arrays
Internal Config data structure reorganisation on top of the new AssocArray helper module it now uses plain associative arrays as internal data structure to make debugging easier
added nonce ValueHolder to DomQuery (console.log(element.nonce.value) and element.nonce.value = "nonceValue"), or element.nonce.isPresent()
Fixing shallow copy in AssocArray
Version 0.40.0
Update to typescript 6.0 in preparation for typescript 7
Version 0.40.0-beta.3
Breaking change:
Messaging moved out of the default mona-dish package entry and is now exposed via mona-dish/messaging.
This keeps the default package entry free of the RxJS-backed messaging layer. Applications using messaging must import from mona-dish/messaging and provide rxjs themselves.
Build and test changes:
Removed the runtime build/test dependency on
ts-node; TypeScript config files and tests now run throughtsx.Webpack keeps using
webpack.config.ts, but bundle builds now run with fullts-loadertype checking.npm run buildrunsverifyfirst, then docs, declarations, bundles, dist copy, and dist smoke tests.Added
npm run smoketo verify generated dist module formats.Added manual
npm run package:testto pack the package isolatedly and verify core usage without RxJS plus messaging usage with explicitly provided RxJS.Fixed Beta 2 - beta 3
- Beta 2 and older - Webpack runtime: aliased mona-dish → src/main/typescript/index_core.ts (TS source, compiled by ts-loader) — same as now - TypeScript type-checking: package had no useful native types entry, so modules.d.ts declared an ambient module "mona-dish" override and the tsconfig paths redirected to the shim, which re-exported from dist/typescript/index_core.ts (TS source alongside .d.ts files in the same dir) - Beta 3 (new): - Webpack runtime: same — src/main/typescript/index_core.ts via the alias, nothing changed - TypeScript type-checking: beta 3 added a proper "types": "./dist/types/index_core.d.ts" condition to the package.json exports map, so TypeScript resolves it natively
Version 0.50.0-beta.8
Performance (deep selector lookups):
_querySelectorAllnow pushes each NodeList straight into a single target array viapushChunkedinstead ofnodes = nodes.concat(objToArray(res)), eliminating the per-rootobjToArraycopy and theconcatreallocation (two redundant full copies of large result sets, e.g. thequerySelectorAll("*")shadow scan inquerySelectorAllDeep).querySelectorAllDeepcollects shadow hosts in a single pass (new private helper_collectShadowRoots) instead ofquerySelectorAll("*").shadowRoot, which materialized a DomQuery wrapping every element and then re-walked it through theshadowRootgetter. The cost stays O(number of elements) — there is no selector for "has a shadow root" — but the throwaway all-elements DomQuery and the second traversal are gone. Behaviour is unchanged, covered by a new nested-shadow-root regression test.childNodesnowpushChunkeds each root's child list into one target array instead ofchildNodeArr = childNodeArr.concat(objToArray(item.childNodes))per root, removing the per-root copy and the O(roots × children) accumulator reallocation.byTagName(includeRootbranch) appends the matching roots viapushChunkedinstead ofreduce((acc, item) => acc.concat([item])), which reallocated the accumulator on every match (O(matches²)). Both are covered by new multi-root regression tests.
Version 0.50.0-beta.6
Bug fix (caret restore on non-text inputs):
- Fixed
Uncaught InvalidStateError: Failed to execute 'setSelectionRange' on 'HTMLInputElement'thrown byDomQuery.setCaretPositionwhen a checkbox or radio button is re-rendered (e.g. via a JSF/Faces Ajax partial update that focuses the re-rendered control).setSelectionRangeexists on everyHTMLInputElementbut the DOM spec mandates it throw for input types without text selection (checkbox,radio,button,file, …); the prior existence-only guard was therefore insufficient. The call is now wrapped so the error is swallowed while the precedingfocus()still applies (silent fail as documented). Regression introduced together with the caret-restore refocus logic.
Version 0.50.0-beta.5
Bug fix (browser argument-stack limit on large arrays):
- Fixed
RangeError: Maximum call stack size exceededwhen collections beyond the engine argument-stack limit (~65k elements in Chromium, varies per engine) flow through the library. All internal call-position spreads of unbounded arrays (fn(...data),new Cls(...data),push(...data),Element.prepend(...data)) were replaced by chunk-safe copies (chunk size 30000, seeMAX_ARG_LENGTH). - New chunk-safe entry points for large data:
Es2019ArrayFrom(arrayLike),pushChunked(target, source),Stream.ofArr(array),LazyStream.ofArr(array),ArrayStreamDataSource.ofArray(array). The variadic forms (Stream.of(...arr)etc.) remain unchanged but are inherently subject to the engine argument limit when the caller spreads — use theofArr-style factories for large arrays. DomQuerynow accepts plain element arrays directly (new DomQuery(elementArray)) and flattens them chunk-safely (the constructor signature always advertised this).DomQuery.prepend/prependToprepend in reverse chunk order — same resulting element order as before, still a single nativeprependcall below the chunk size.- Fixed
DomQuery.offsetTopreturningNaN(long-standing missing spread in the reduction). - Added the regression suite
LargeArrayChunkingTest.spec.ts: 200k-element runs over all chunking primitives with full order/completeness verification, 70k-elementDomQueryconstruction, 35k chunk-boundaryprependorder check.
Version 0.50.0-beta.4
Cleanup:
DomQuery.getCaretPosition: removed the legacy IEdocument.selection/TextRangefallback branch and rewrote the method as a clean-roomselectionStart-only implementation. Caret read-out works on IE9+ and all modern browsers; IE8 and older (which never supportedselectionStart) are no longer handled.
Version 0.50.0-beta.3
Bug fix:
DomQuery.outerHTML/ caret handling: fixed a regression where a partial update of an unrelated component reset the caret of a different, still-focused input field to position 0. The caret is now only saved/restored when the focused element is actually part of the subtree being replaced.DomQuery.getCaretPosition: prefer the modernselectionStartproperty when available, falling back to the legacy IEdocument.selectionrange only when it is not. This makes caret read-out reliable across partial-update cycles (e.g. the Tobago<tc:in>typing scenario).- Added regression tests covering caret survival across partial updates and in-subtree restore.
Version 0.50.0-beta.2
Bug fixes and test coverage improvements:
Lang.objAssignfallback path: fixed spec non-compliance — now copies enumerable Symbol-keyed own properties viaObject.getOwnPropertySymbols+propertyIsEnumerable, matching ES2015Object.assignbehaviourDomQuery.fromMarkup: fixedtfootbranch (was using wrong table index); unifiedthead/tbody/tfootinto a single branch; addedthcell handling (previously fell through to default)DomQuery.setCaretPosition: fixed typosetSelectiongRange→setSelectionRange(caret positioning was silently doing nothing)DomQuery.firstElem/lastElem: fixed guard condition> 1→> 0(single-element DomQuery was skipped); fixedlastElempassing wrong index to callbackDomQuery.elements/deepElements: removed invalidcheckboxCSS selector (dead weight — checkboxes are already matched byinput)DomQuerylegacywaitUntilDomfallback: movedtimeout/intervaldeclarations before assignment for clarityStyle.getClass()/Style.fromNullable(): fixed both returning/creatingElementAttributeinstead ofStyleElementAttribute.set value(): removed duplicatesetAttributecall after loop- Added test coverage for:
objAssign(12 tests),fromMarkupall branches (14 tests),setCaretPosition(3 tests),firstElem/lastElem(6 tests),Style.fromNullable/getClass(3 tests),waitUntilDomfast-path + tightened assertions
