# 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](https://github.com/werpu/mona-dish/blob/master/docs/Optional.md)  ... a class which is derived from Javas optional but also encapsulates elvis operator like accessors
                to cut down on code
* [ValueEmbedder](https://github.com/werpu/mona-dish/blob/master/docs/ValueEmbedder.md) ... 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](https://github.com/werpu/mona-dish/blob/master/docs/Stream.md) ... a typescript based implementation of early and lazily evaluating streams                   
* [DomQuery](https://github.com/werpu/mona-dish/blob/master/docs/DomQuery.md) ... 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](https://github.com/werpu/mona-dish/blob/master/docs/Messaging.md) ... a messaging bus which can break page isolation levels to allow communication between iframes/popups/shadow dom/dom, exposed via `mona-dish/messaging`
* [RxJS](https://github.com/werpu/mona-dish/blob/master/docs/RxJS.md) ... 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`:

```typescript
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:

* [Optional Documentation](https://github.com/werpu/mona-dish/blob/master/docs/Optional.md)


  
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

* [ValueEmbedder Documentation](https://github.com/werpu/mona-dish/blob/master/docs/ValueEmbedder.md)

 
  
### Configuration  

* [Configuration Documentation](https://github.com/werpu/mona-dish/blob/master/docs/Configuration.md)


### 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


```Typescript
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

```Typescript
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](https://github.com/werpu/mona-dish/blob/master/src/test/typescript/StreamTest.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](https://github.com/werpu/mona-dish/blob/master/docs/DomQuery.md) ... 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:](https://github.com/werpu/mona-dish/blob/master/docs/Messaging.md)

Messaging is not exported by the default `mona-dish` package entry anymore. Use:

```typescript
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

* [Streams](https://github.com/werpu/mona-dish/blob/master/docs/Stream.md)
* [RxJS](https://github.com/werpu/mona-dish/blob/master/docs/RxJS.md)


## Examples

Various usage examples can be found in the tests:

* [DomQuery, XmlQuery](https://github.com/werpu/mona-dish/blob/master/src/test/typescript/DomQueryTest.ts)
* [Optional, ValueEmbedder, Config](https://github.com/werpu/mona-dish/blob/master/src/test/typescript/MonadTest.ts)
* [Promise](https://github.com/werpu/mona-dish/blob/master/src/test/typescript/PromiseTest.ts)
* [Stream](https://github.com/werpu/mona-dish/blob/master/src/test/typescript/StreamTest.ts)
                   


# 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 through `tsx`.
* Webpack keeps using `webpack.config.ts`, but bundle builds now run with full `ts-loader` type checking.
* `npm run build` runs `verify` first, then docs, declarations, bundles, dist copy, and dist smoke tests.
* Added `npm run smoke` to verify generated dist module formats.
* Added manual `npm run package:test` to 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):

* `_querySelectorAll` now pushes each NodeList straight into a single target array via
  `pushChunked` instead of `nodes = nodes.concat(objToArray(res))`, eliminating the per-root
  `objToArray` copy and the `concat` reallocation (two redundant full copies of large result
  sets, e.g. the `querySelectorAll("*")` shadow scan in `querySelectorAllDeep`).
* `querySelectorAllDeep` collects shadow hosts in a single pass (new private helper
  `_collectShadowRoots`) instead of `querySelectorAll("*").shadowRoot`, which materialized a
  DomQuery wrapping every element and then re-walked it through the `shadowRoot` getter. 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.
* `childNodes` now `pushChunked`s each root's child list into one target array instead of
  `childNodeArr = childNodeArr.concat(objToArray(item.childNodes))` per root, removing the
  per-root copy and the O(roots × children) accumulator reallocation.
* `byTagName` (`includeRoot` branch) appends the matching roots via `pushChunked` instead of
  `reduce((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 by `DomQuery.setCaretPosition` when a checkbox or radio button is re-rendered (e.g. via
  a JSF/Faces Ajax partial update that focuses the re-rendered control). `setSelectionRange`
  exists on every `HTMLInputElement` but 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 preceding
  `focus()` 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 exceeded` when 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, see `MAX_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 the `ofArr`-style factories for large arrays.
* `DomQuery` now accepts plain element arrays directly (`new DomQuery(elementArray)`) and
  flattens them chunk-safely (the constructor signature always advertised this).
* `DomQuery.prepend` / `prependTo` prepend in reverse chunk order — same resulting element
  order as before, still a single native `prepend` call below the chunk size.
* Fixed `DomQuery.offsetTop` returning `NaN` (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-element `DomQuery`
  construction, 35k chunk-boundary `prepend` order check.

### Version 0.50.0-beta.4

Cleanup:

* `DomQuery.getCaretPosition`: removed the legacy IE `document.selection` / `TextRange`
  fallback branch and rewrote the method as a clean-room `selectionStart`-only
  implementation. Caret read-out works on IE9+ and all modern browsers; IE8 and older
  (which never supported `selectionStart`) 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 modern `selectionStart` property when available,
  falling back to the legacy IE `document.selection` range 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.objAssign` fallback path: fixed spec non-compliance — now copies enumerable Symbol-keyed own properties via `Object.getOwnPropertySymbols` + `propertyIsEnumerable`, matching ES2015 `Object.assign` behaviour
* `DomQuery.fromMarkup`: fixed `tfoot` branch (was using wrong table index); unified `thead`/`tbody`/`tfoot` into a single branch; added `th` cell handling (previously fell through to default)
* `DomQuery.setCaretPosition`: fixed typo `setSelectiongRange` → `setSelectionRange` (caret positioning was silently doing nothing)
* `DomQuery.firstElem` / `lastElem`: fixed guard condition `> 1` → `> 0` (single-element DomQuery was skipped); fixed `lastElem` passing wrong index to callback
* `DomQuery.elements` / `deepElements`: removed invalid `checkbox` CSS selector (dead weight — checkboxes are already matched by `input`)
* `DomQuery` legacy `waitUntilDom` fallback: moved `timeout`/`interval` declarations before assignment for clarity
* `Style.getClass()` / `Style.fromNullable()`: fixed both returning/creating `ElementAttribute` instead of `Style`
* `ElementAttribute.set value()`: removed duplicate `setAttribute` call after loop
* Added test coverage for: `objAssign` (12 tests), `fromMarkup` all branches (14 tests), `setCaretPosition` (3 tests), `firstElem`/`lastElem` (6 tests), `Style.fromNullable`/`getClass` (3 tests), `waitUntilDom` fast-path + tightened assertions


