Transform-Signal-Executor framework for Reactive Streams

Related tags

Vue.js core
Overview

TSERS

Transform-Signal-Executor framework for Reactive Streams (RxJS only at the moment... 😞 ).

Travis Build Code Coverage NPM version Gitter GitHub issues

"tsers!"

Pronunciation: [tsers] (Also note that the /r/ is an alveolar trill, or "rolled r", not the postalveolar approximant ɹ̠ that is used in English and sometimes written similarly for convenience.)

Motivation

In the era of the JavaScript fatigue, new JS frameworks pop up like mushrooms after the rain, each of them providing some new and revolutionary concepts. So overwhelming! That's why TSERS was created. It doesn't provide anything new. Instead, it combines some old and well-known techniques/concepts and packs them into single compact form suitable for the modern web application development.

Technically the closest relative to TSERS is Cycle.js, but conceptually the closest one is CALM^2. Roughly it could be said that TSERS tries to combine the excellent state consistency maintaining strategies from CALM^2 and explicit input/output gates from Cycle - the best from both worlds.

Hello TSERS!

The mandatory "Hello world" applicatin with TSERS:

import {Observable as O} from "rx"
import TSERS from "@tsers/core"
import ReactDOM from "@tsers/react"
import Model from "@tsers/model"

function main(signals) {
  const {DOM, model$: text$, mux} = signals
  const {h} = DOM

  const vdom$ = DOM.prepare(text$.map(text =>
    h("div", [
      h("h1", text),
      h("button", "Click me!")
    ])))

  const click$ = DOM.events(vdom$, "button", "click")
  const updateMod$ = text$.mod(
    click$.map(() => text => text + "!")
  )

  return mux({
    DOM: vdom$,
    model$: updateMod$
  })
}

TSERS(main, {
  DOM: ReactDOM("#app"),
  model$: Model("Tsers")
})

Core concepts

TSERS applications are built upon the three following concepts

  1. Signals flowing through the application
  2. Signal Transform functions transforming input signals into output signals
  3. Executors performing effects based on the output signals

Signals

Signals are the backbone of TSERS application. They are the only way to transfer inter-app information and information from main to interpreters and vice versa. In TSERS applications, signals are modeled as (RxJS) observables.

  • Observables are immutable so the defined control flow is always explicit and declarative
  • Observables are first-class objects so they can be transformed into other observables easily by using higher-order functions

TSERS relies entirely on (RxJS) observables and reactive programming so if those concepts are not familiar, you should take a look at some online resources or books before exploring TSERS. One good online tutorial to RxJS can be found here.

TODO: muxing and demuxing

Signal transforms

Assuming you are somehow familiar with RxJS (or some other reactive library like Kefir or Bacon.js), you've definitely familiar with signal transform functions.

The signature of signal transform function f is:

f :: (Observable A, ...params) => Observable B

So basically it's just a pure function that transforms an observable into another observable. So all observable's higher order functions like map, filter, scan (just to name a few) are also signal transformers.

Let's take another example:

function titlesWithPrefix(item$, prefix) {
  return item$
    .map(it => it.title)
    .filter(title => title.indexOf(prefix) === 0)
}

titlesWithPrefix is also a signal transform function: it takes an observable of items and the prefix that must match the item title and returns an observable of item titles having the given prefix.

titlesWithPrefix :: (Observable Item, String) => Observable String

And as you can see, titlesWithPrefix used internally two other signal transform functions: map and filter. Because signal transform functions are pure, it's trivial to compose and reuse them in order to create the desired control flow from input signals to output signals.

If the signals are the backbone of TSERS applications, signal transformers are the muscles around it and moving it.

Executors

After flowing through the pure signal transformers, the transformed output signals arrive to the executors. In TSERS, executors are also functions. But not pure. They are functions that do nasty things: cause side-effects and change state. That is, executors' signature looks like this:

executor :: Observable A => Effects

Let's write an executor for our titles:

function alertNewTitles(title$) {
  title$.subscribe(title => {
    alert(`Got new title! ${title}`)
  })
}

And what this makes executors by using the previous analogy... signals flowing through the backbone down and down and finally to the... anus 💩 . Yeah, unfortunately the reality is that somewhere in the application you must do the crappy part: render DOM to the browser window, modify the global state etc. In TSERS applications, this part falls down to executors.

But the good news is that these crappy things are (usually) not application specific and easily generalizable! That's why TSERS has the interpreter abstraction.

Application structure

As told before, every application inevitably contains good parts and bad parts. And that's why TSERS tries to create an explicit border between those parts: the interpreter abstraction.

The good (pure) parts are inside the signal transform function main, and the bad parts are encoded into interpreters.

Conceptually the full application structure looks like:

function main(input$) {
  // ... app logic ...
  return output$
}
interpreters = makeInterpreters()
output$ = main(interpreters.signals())
interpreters.execute(output$)

main

main function is the place where you should put the application logic in TSERS application. It describes the user interactions and as a result of those interactions, provides an observable of output signals that are passed to the interpreters' executor functions.

That is, main is just another signal transform function that receives some core transform functions (explained later) plus input signals and other transform functions from interpreters. By using those signals and transforms, main is able to produce the output signals that are consumed by the interpreter executors.

Interpreters

Interpreters are not a new concept: they come from the Free Monad Pattern. In common language (and rounding some edges) interpreters are an API that separates the representation from the actual computation. If you are interested in Free Monads, I recommend to read this article.

In TSERS, interpreters consist of two parts:

  1. Input signals and/or signal transforms
  2. Executor function

Input signals and signal transforms are given to the main. They are a way for interpreter to encapsulate the computation from the representation. For example HTTP interpreter provides the request transform. It takes an observable of request params and returns an observable of request observables (request :: Observable params => Observable (Observable respose)).

Now the main can use that transform:

function main({HTTP}) {
  const click$ = ....
  const users$ = HTTP.request(click$.map(() => ({url: "/users", method: "get"})).switch()
  // ...
}

Note that main doesn't need to know the actual details what happens inside request - it might create the request by using vanilla JavaScript, superagent or any other JS library. It may not even make a HTTP request every time when the click happens but returns a cached result instead! It's not main's business to know such things.

Some interactions may produce output signals that are not interesting in main. That's why interpreters have also possibility to define an executor function which receives those output signals and interprets them, (usually) causing some (side-)effects.

Let's take the DOM interpreter as an example. main may produce virtual dom elements as output signals but it's not interested in how (or where) those virtual dom elements are rendered.

function main({DOM}) {
  const {h} = DOM 
  return DOM.prepare(Observable.just(h("h1", "Tsers!")))
}

What to put into main and what into interpreters?

In a rule of thumb, you should use interpreter if you need to produce some effects. Usually this reduces into three main cases:

  1. You need to use Observable's .subscribe - you should never need to use that inside the main
  2. You need to communicate with the external world somehow
  3. You need to change some global state

Encoding side-effects into signal transforms or output signals?

In a rule of thumb, you should encode the side-effects into signal transform functions if the input signal and the side effect result signal have a direct causation, for example request => response.

You should encode the side-effects into output signals and interpret them with the executor when the input there is no input => output causation (only effects), for example VNode => ().

Why the separation of main and interpreters?

You may think that the separation of main and interpreters is just waste. What benefit you get by doing that? The answer is that separating those significantly improves testability, extensibility and the separation of concerns of the application.

Imagine that you need to implement universal server rendering to your application - just change the DOM interpreter to server DOM interpreter that produces HTML strings instead of rendering the virtual dom to the actual DOM. How about if you need to test your application? Just replace the interpreters with test interpreters so that they produce signals your test case needs and assert the output signals your application produces. How about if you need to implement undo/redo? Just change the application state interpreter to keep state revisions in memory. How about if you API version changes? Just modify you API interpreter to convert the new version data to the current one.

From theory to practice

Now that you're familiar with TSER's core concepts and the application structure, let's see how to build TSERS application in practice. This section is just a quick introduction. For more detailed tutorial, please take a look at the TSERS tutorial in the examples repository.

Starting the application

First you need to install @tsers/core and some interpreters. We're gonna use two basic interpreters: React DOM interpreter for rendering and Model interpreter for our application state managing.

npm i --save @tsers/core @tsers/react @tsers/model

Now we can create and start our application. @tsers/core provides a function that takes the main and the interpreters that are attached to the application. The official interpreter packages provide always a factory function that can be used to initialize the actual interpreter.

import TSERS from "@tsers/core"
import ReactDOM from "@tsers/react"
import Model from "@tsers/model"

function main(signals) {
  // your app logic comes here!
}

// start the application with model$ and DOM interpreters
TSERS(main, {
  DOM: ReactDOM("#app"),     // render to #app element
  model$: Model(0)           // create application state model by using initial value: 0
})

Adding some app logic inside the main

Now we can use the signals and transforms provided by those interpreters, as well as TSERS's core transform functions (see API reference below). Interpreters' signals and transform functions are always accessible by their keys. Also main's output signals match those keys:

function main(signals) {
  // All core transforms (like "mux") are also accessible 
  // via "signals" input parameter
  const {DOM, model$, mux} = signals
  const {h} = DOM

  // model$ is an instance of @tsers/model - it provides the application
  // state as an observable, so you can use model$ like any other observable
  // (map, filter, combineLatest, ...).

  // let's use the model$ observable to get its value and render a virtual-dom
  // based on the value. DOM.prepare is needed so that we can derive user event
  // streams from the virtual dom stream
  const vdom$ = DOM.prepare(model$.map(counter =>
    h("div", [
      h("h1", `Counter value is ${counter}`),
      h("button.inc", "++"),
      h("button.dec", "--")
    ])))

  // model$ enables you to change the state by emitting "modify functions"
  // as out output signals. The modify functions have always signature
  // (curState => nextState) - they receive the current state of the model
  // as input and must provide the next state based on the current state

  // Let's make modify functions for the counter: when increment button is
  // clicked, increment the counter state by +1. When decrement button is clicked,
  // decrement the state by -1
  const incMod$ = DOM.events(vdom$, ".inc", "click").map(() => state => state + 1)
  const decMod$ = DOM.events(vdom$, ".dec", "click").map(() => state => state - 1)

  // And because the mods are just observables, we can merge them
  const mod$ = O.merge(incMod$, decMod$)

  // Finally we must produce the output signals. Because JavaScript functions
  // can return only one value (observable), we must multiplex ("mux") DOM
  // and model$ signals into single observable by using "mux" core transform
  return mux({
    DOM: vdom$,
    model$: model$.mod(mod$)
  })
}

Again: more detailed tutorial can be found from the TSERS examples repository.

What's different compared to Cycle?

If you read through this documentation, you might wonder that TSERS resembles Cycle very much. Technically that's true. Then why not to use Cycle?

Although the technical implementations of TSERS and Cycle are very similar, their ideologies are not. Cycle is strongly driven by the classification of read-effects and write-effects which means that drivers are not "allowed" to provide signal transforms encoding side-effects. Instead, all side effects must go to sinks and their results must be read from the sources, regardless of the causation of the side-effect and it's input.

Cycle's drivers are also meant for external world communications only, hence e.g. maintaining the global application state with drivers is not "allowed" in Cycle (although maintaining it with e.g. Relay driver is!!).

In practice, those features in Cycle result in some unnecessary symptoms like the existence of isolation, usage of IMV instead of MVI (which works pretty well btw, until your intents start to depend on the model), proxy subjects usage, performance issues and unnecessary complexity whe sharing the state between parent and child components.

And those are the reasons for the existence of TSERS.

Core transforms API Reference

mux

JavaScript allows function to return only one value. That means that main can return only one observable of signals. However, applications usually produce multiple types of signals (DOM, WebSocket messages, model state changes...).

That's why TSERS uses multiplexing to "combine" multiple types of signals into single observable. Multiplexing is way of combining multiple signal streams into one stream of signals so that different type of signals are identifiable from other signals.

The signature of mux is:

mux :: ({signalKey: signal$}, otherMuxed$ = Observable.empty()) => muxedSignal$

mux takes the multiplexed streams as an object so that object's keys represent the type of the multiplexed signals. mux takes also second (optional) parameter, that is a stream of already muxed other signals (coming usually from the child components) and merges it to output.

Usually you want to use mux in the end of main to combine all application signals into single observable of signals:

function main({DOM, model$}) {
  // ....
  return mux({
    DOM: vdom$,
    model$: mod$
  })
}

demux

De-muxing (or de-multiplexing) is the reverse operation for muxing: it takes an observable of the muxed signals, extracts the given signal types by their keys and returns also the rest of the signals that were not multiplexed

demux :: (muxedSignal$, ...keys) => [{signalKey: signal$}, otherMuxed$]

Usually you want to use this when you call child component from the parent component and want to post-process child's specific output signals (e.g. DOM) in the parent component:

const childOut$ = Counter({...signals, model$: childModel$})
const [{DOM: childDOM$}, rest$] = demux(childOut$, "DOM")

loop

loop is a transform that allows "looping" signals from downstream back to upstream. It takes input signals and a transform function producing output$ and loop$ signals array - output$ signals are passed through as they are, but loop$ signals are merged back to the transform function as input signals.

const initialText$ = O.just("Tsers").shareReplay(1)
const vdom$ = loop(initialText$, text$ => {
  const vdom$ = DOM.prepare(text$.map(...))
  const click$ = DOM.events(vdom$, "button", "click")
  const updatedText$ = click$.withLatestFrom(text$, (_, text) => text + "!")
  // vdom$ signals are passed out, updatedText$ signals are looped back to text$ signals
  return [vdom$, updatedText$]
})

mapListById

Takes a list observable (whose items have id property) and iterator function, applies the iterator function to each list item and returns a list observable by using the return values from the iterator function (conceptually same as list$.map(items => items.map(...))).

  • Item ids must be unique within the list.
  • Iterator function receives two arguments: iterated item id and an observable containing the item and it's state changes

ATTENTION: iterator function is applied only once per item (by id), although the list observable emits multiple values. This enables some heavy performance optimizations to the list processing like duplicate detection, cold->hot observable conversion and caching.

TODO: example...

mapListBy

Same as mapListById but allows user to define custom identity function instead of using id property. Actually the mapListById is just a shorthand for this transform:

const mapListById = mapListBy(item => item.id)

demuxCombined

demuxCombined has the same API contract as demux but instead of bare output signals, demuxCombined handles a list of output signals. The name already implies the extraction strategy: after the output signals are extracted by using the given keys, their latest values are combined by using Observable.combineLatest, thus resulting an observable that produces a list of latest values from the extracted output signals. Rest of the signals are flattened and merged by using Observable.merge so the return value of demuxCombined is identical with demux (hence can be used in the same way when muxing child signals to parent's output signals).

TODO: example...

Interpreter API reference

TODO: ...

License

MIT

Logo by Globalicon (CC BY 3.0)

Comments
  • Event listener binding bug in React DOM interpreter

    Event listener binding bug in React DOM interpreter

    I was trying to port this code to TSERS. As you can see, it's a very simple drag&drop example wrtitten with rxjs. My code looks like this:

    import {Observable as O} from "rx"
    
    export default function main(signals) {
      const {DOM: {h, prepare, events}, model$, mux} = signals
    
      const vdom$ = prepare(model$.map((state) =>
        h('div', {
          id: 'mydiv',
          style: {
            backgroundColor: 'black',
            width: '70px',
            height: '70px',
            display: 'inline-block',
            position: 'absolute',
            cursor: 'move',
            top: state.top + 'px',
            left: state.left + 'px'
          }
        })
      ))
    
      const mousedown$ = events(vdom$,'#mydiv', 'mousedown')
      const mouseup$ = events(vdom$, '#mydiv', 'mouseup')
      const mousemove$ = events(vdom$, null, 'mousemove')
    
      const mousedrag$ = mousedown$
        .flatMap(function(md) {
          const
            startX = md.nativeEvent.offsetX,
            startY = md.nativeEvent.offsetY;
    
          return mousemove$.map(mm => {
            mm.preventDefault();
    
            return {
              left: mm.clientX - startX,
              top: mm.clientY - startY
            };
          }).takeUntil(mouseup$);
        })
    
      const mod$ = mousedrag$.map(pos => state => pos)
    
      return mux({
        DOM: vdom$,
        model$: model$.mod(mod$)
      })
    }
    

    It works OK except the first time you "mouseup" (free mouse button). These are the steps to reproduce this issue:

    1. mousedown over item (mydiv) and mouseup (=mouseclick)
    2. move your mouse. mydiv moves!!! Item sould not move if you are not pressing your mouse button. If you try it again (without reset app, F5), it works OK. It's like the program listen all mouseup events but the first one. I used ReactDOM. I've tried Snabbdom interpreter but it doesn't work (no styles?).

    Thank you in advance.

    Bug DOM Driver 
    opened by pmros 5
  • Better documentation/API

    Better documentation/API

    This is not an urgent issue, but I log it here to keep it in mind.

    I got around the TSERS API and idea relatively quick (and yet not that quick) because I usually wire my cycles by hand and I have a similar API (conceptually similar but the API is quite different), so it was not so hard to see the abstraction. However, it would really help to define explain TSERS not in function of cycle (which would assume it is destined to people who know cycle), but in function of functional reactive programming, and your app as a circuit. Rxjs operators are basic signal transformers, components are composite signal transformers made from basic signal transformers, and you add the feedback loop with a function (run, is it? but that would more be sth like connect or wire?), and you switch on the circuit with execute (I'd rather use a verb than a noun, that would be the real run in my understanding).

    I approached this in my case as:

    • specification
    • runtime

    In the specification part, you have three concepts:

    • signals
    • signal transformers
    • feedback loop

    from which you derive :

    • component (logic controller? integrated circuit? they are distinguished from the lower-level entities because they can feature feedback loop)
    • application

    through composition.

    In the runtime part, you have:

    • parametrization of initialization order
    • orderly subscription of signals/component/application
    • orderly disposal of signals/component/application

    And the API should in my opinion relate to that conceptual map rather than looking for brevity.

    Core Discussion Docs 
    opened by brucou 4
  • Intercommunication between multiple TSERS applications

    Intercommunication between multiple TSERS applications

    I have a fairly large module that I want to break into smaller independent micro-services that can communicate with each other. Each service would have its own transformations and executors and eventually communicate via signals.

    I think TSERS would make a lot of sense in this particular case, except that the TSERS function returns a disposable instead of a stream. I am not sure how it should be designed and what stream should it exactly be returning.

    Core Discussion 
    opened by tusharmath 2
  • Version 1.x rewrite

    Version 1.x rewrite

    Hello Folks!

    Based on the feedback I've received, I'm gonna do a total re-write of TSERS for version 1.x. Don't be scared, that might sound bigger deal than it actually is. The goal of this re-write is to simplify the API surface and establish better and more understandable terminology without sacrificing the flexibility of the TSERS architecture.

    The changes are following:

    • Instead of driver, use interpreter
      • The naming comes from free monad pattern
      • I don't want to restrict interpreter usage to only side-effects (which name "driver" implies), interpreters are only a way to interpret output signals from your application (e.g. in browser, virtual DOM is converted to actual DOM, in server it's converted to HTML string)
    • Rename transducers to (signal) transformers (the former sounds intimidating based on the feedback :smile:)
    • Simplify main signature from main :: T => in$ => out$ to main :: in$ => out$
      • The curried form was to complicated for beginners and didn't provide so much benefits in practice as I anticipated
      • in$ is not a pure signal stream anymore but it contains both (possible) input$ signals and transformers by interpreter
    • Simplify interpreter signature from interpreter :: Config => [signal$, T, executor] to interpreter :: Config => [signal$, executor]
      • Again, signal$ contains both input signals (if any) and signal transformers
    • Make app initialization simpler: const dispose = TSERS(main, { ...interpreters})
    • Rename common transforms:
      • decompose to demux
      • compose to mux
      • run to loop
    • Remove lift* transforms
    • Add common transform: mapListBy based on this implementation
    • Add model interpreter and encourage its usage in examples/tutorials
    • Re-write docs to use the new terminology and get rid of Cycle.js references

    Each project is going to get 1.x branch that will be merged into master once the re-write is complete

    Refactoring Core Driver Docs 
    opened by milankinen 2
  • Rename compose and decompose

    Rename compose and decompose

    @brucou pointed out that signal processing alternatives to compose and decompose are multiplex and demultiplex.

    This sounds good. At least we can create aliases to those and perhaps finally remove the original names completely.

    How about shortening the names to mux and demux?

    Refactoring Core 
    opened by milankinen 1
  • Implement HTTP driver

    Implement HTTP driver

    Use superagent for actual request sending.

    Driver contract:

    function makeHTTPDriver(baseUrl = "") {
       const Transducers = { request :: req$ => res$$ }
       return [Transducers]
    }
    
    Feature HTTP Driver 
    opened by milankinen 1
  • Bump eslint from 2.5.3 to 6.6.0

    Bump eslint from 2.5.3 to 6.6.0

    Bumps eslint from 2.5.3 to 6.6.0.

    Release notes

    Sourced from eslint's releases.

    v6.6.0

    • 39dfe08 Update: false positives in function-call-argument-newline (fixes #12123) (#12280) (Scott O'Hara)
    • 4d84210 Update: improve report location for no-trailing-spaces (fixes #12315) (#12477) (Milos Djermanovic)
    • c6a7745 Update: no-trailing-spaces false negatives after comments (fixes #12479) (#12480) (Milos Djermanovic)
    • 0bffe95 Fix: no-misleading-character-class crash on invalid regex (fixes #12169) (#12347) (Milos Djermanovic)
    • c6a9a3b Update: Add enforceForIndexOf option to use-isnan (fixes #12207) (#12379) (Milos Djermanovic)
    • 364877b Update: measure plugin loading time and output in debug message (#12395) (Victor Homyakov)
    • 1744fab Fix: operator-assignment removes and duplicates comments (#12485) (Milos Djermanovic)
    • 52ca11a Fix: operator-assignment invalid autofix with adjacent tokens (#12483) (Milos Djermanovic)
    • 0f6d0dc Fix: CLIEngine#addPlugin reset lastConfigArrays (fixes #12425) (#12468) (Toru Nagashima)
    • 923a8cb Chore: Fix lint failure in JSDoc comment (#12489) (Brandon Mills)
    • aac3be4 Update: Add ignored prop regex no-param-reassign (#11275) (Luke Bennett)
    • e5382d6 Chore: Remove unused parameter in dot-location (#12464) (Milos Djermanovic)
    • 49faefb Fix: no-obj-calls false positive (fixes #12437) (#12467) (Toru Nagashima)
    • b3dbd96 Fix: problematic installation issue (fixes #11018) (#12309) (Toru Nagashima)
    • cd7c29b Sponsors: Sync README with website (ESLint Jenkins)
    • 8233873 Docs: Add note about Node.js requiring SSL support (fixes #11413) (#12475) (Nicholas C. Zakas)
    • 89e8aaf Fix: improve report location for no-tabs (#12471) (Milos Djermanovic)
    • 7dffe48 Update: Enable function string option in comma-dangle (fixes #12058) (#12462) (YeonJuan)
    • e15e1f9 Docs: fix doc for no-unneeded-ternary rule (fixes #12098) (#12410) (Sam Rae)
    • b1dc58f Sponsors: Sync README with website (ESLint Jenkins)
    • 61749c9 Chore: Provide debug log for parser errors (#12474) (Brad Zacher)
    • 7c8bbe0 Update: enforceForOrderingRelations no-unsafe-negation (fixes #12163) (#12414) (Sam Rae)
    • 349ed67 Update: improve report location for no-mixed-operators (#12328) (Chiawen Chen)
    • a102eaa Fix: prefer-numeric-literals invalid autofix with adjacent tokens (#12387) (Milos Djermanovic)
    • 6e7c18d Update: enforceForNewInMemberExpressions no-extra-parens (fixes #12428) (#12436) (Milos Djermanovic)
    • 51fbbd7 Fix: array-bracket-newline consistent error with comments (fixes #12416) (#12441) (Milos Djermanovic)
    • e657d4c Fix: report full dot location in dot-location (#12452) (Milos Djermanovic)
    • 2d6e345 Update: make isSpaceBetweenTokens() ignore newline in comments (#12407) (YeonJuan)
    • 84f71de Update: remove default overrides in keyword-spacing (fixes #12369) (#12411) (YeonJuan)
    • 18a0b0e Update: improve report location for no-space-in-parens (#12364) (Chiawen Chen)
    • d61c8a5 Update: improve report location for no-multi-spaces (#12329) (Chiawen Chen)
    • 561093f Upgrade: bump inquirer to ^7.0.0 (#12440) (Joe Graham)
    • fb633b2 Chore: Add a script for testing with more control (#12444) (Eric Wang)
    • 012ec51 Sponsors: Sync README with website (ESLint Jenkins)
    • 874fe16 New: pass cwd from cli engine (#12389) (Eric Wang)
    • b962775 Update: no-self-assign should detect member expression with this (#12279) (Tibor Blenessy)
    • 02977f2 Docs: Clarify eslint:recommended semver policy (#12429) (Kevin Partington)
    • 97045ae Docs: Fixes object type for rules in "Use a Plugin" (#12409) (Daisy Develops)
    • 24ca088 Docs: Fix typo in v6 migration guide (#12412) (Benjamim Sonntag)
    • b094008 Chore: update version parameter name (#12402) (Toru Nagashima)
    • e5637ba Chore: enable jsdoc/require-description (#12365) (Kai Cataldo)
    • d31f337 Sponsors: Sync README with website (ESLint Jenkins)
    • 7ffb22f Chore: Clean up inline directive parsing (#12375) (Jordan Eldredge)
    • 84467c0 Docs: fix wrong max-depth example (fixes #11991) (#12358) (Gabriel R Sezefredo)
    • 3642342 Docs: Fix minor formatting/grammar errors (#12371) (cherryblossom000)
    • c47fa0d Docs: Fix missing word in sentence (#12361) (Dan Boulet)
    • 8108f49 Chore: enable additional eslint-plugin-jsdoc rules (#12336) (Kai Cataldo)
    • b718d2e Chore: update issue template with --eslint-fix flag (#12352) (James George)
    • 20ba14d Sponsors: Sync README with website (ESLint Jenkins)
    ... (truncated)
    Changelog

    Sourced from eslint's changelog.

    v6.6.0 - October 25, 2019

    • 39dfe08 Update: false positives in function-call-argument-newline (fixes #12123) (#12280) (Scott O'Hara)
    • 4d84210 Update: improve report location for no-trailing-spaces (fixes #12315) (#12477) (Milos Djermanovic)
    • c6a7745 Update: no-trailing-spaces false negatives after comments (fixes #12479) (#12480) (Milos Djermanovic)
    • 0bffe95 Fix: no-misleading-character-class crash on invalid regex (fixes #12169) (#12347) (Milos Djermanovic)
    • c6a9a3b Update: Add enforceForIndexOf option to use-isnan (fixes #12207) (#12379) (Milos Djermanovic)
    • 364877b Update: measure plugin loading time and output in debug message (#12395) (Victor Homyakov)
    • 1744fab Fix: operator-assignment removes and duplicates comments (#12485) (Milos Djermanovic)
    • 52ca11a Fix: operator-assignment invalid autofix with adjacent tokens (#12483) (Milos Djermanovic)
    • 0f6d0dc Fix: CLIEngine#addPlugin reset lastConfigArrays (fixes #12425) (#12468) (Toru Nagashima)
    • 923a8cb Chore: Fix lint failure in JSDoc comment (#12489) (Brandon Mills)
    • aac3be4 Update: Add ignored prop regex no-param-reassign (#11275) (Luke Bennett)
    • e5382d6 Chore: Remove unused parameter in dot-location (#12464) (Milos Djermanovic)
    • 49faefb Fix: no-obj-calls false positive (fixes #12437) (#12467) (Toru Nagashima)
    • b3dbd96 Fix: problematic installation issue (fixes #11018) (#12309) (Toru Nagashima)
    • cd7c29b Sponsors: Sync README with website (ESLint Jenkins)
    • 8233873 Docs: Add note about Node.js requiring SSL support (fixes #11413) (#12475) (Nicholas C. Zakas)
    • 89e8aaf Fix: improve report location for no-tabs (#12471) (Milos Djermanovic)
    • 7dffe48 Update: Enable function string option in comma-dangle (fixes #12058) (#12462) (YeonJuan)
    • e15e1f9 Docs: fix doc for no-unneeded-ternary rule (fixes #12098) (#12410) (Sam Rae)
    • b1dc58f Sponsors: Sync README with website (ESLint Jenkins)
    • 61749c9 Chore: Provide debug log for parser errors (#12474) (Brad Zacher)
    • 7c8bbe0 Update: enforceForOrderingRelations no-unsafe-negation (fixes #12163) (#12414) (Sam Rae)
    • 349ed67 Update: improve report location for no-mixed-operators (#12328) (Chiawen Chen)
    • a102eaa Fix: prefer-numeric-literals invalid autofix with adjacent tokens (#12387) (Milos Djermanovic)
    • 6e7c18d Update: enforceForNewInMemberExpressions no-extra-parens (fixes #12428) (#12436) (Milos Djermanovic)
    • 51fbbd7 Fix: array-bracket-newline consistent error with comments (fixes #12416) (#12441) (Milos Djermanovic)
    • e657d4c Fix: report full dot location in dot-location (#12452) (Milos Djermanovic)
    • 2d6e345 Update: make isSpaceBetweenTokens() ignore newline in comments (#12407) (YeonJuan)
    • 84f71de Update: remove default overrides in keyword-spacing (fixes #12369) (#12411) (YeonJuan)
    • 18a0b0e Update: improve report location for no-space-in-parens (#12364) (Chiawen Chen)
    • d61c8a5 Update: improve report location for no-multi-spaces (#12329) (Chiawen Chen)
    • 561093f Upgrade: bump inquirer to ^7.0.0 (#12440) (Joe Graham)
    • fb633b2 Chore: Add a script for testing with more control (#12444) (Eric Wang)
    • 012ec51 Sponsors: Sync README with website (ESLint Jenkins)
    • 874fe16 New: pass cwd from cli engine (#12389) (Eric Wang)
    • b962775 Update: no-self-assign should detect member expression with this (#12279) (Tibor Blenessy)
    • 02977f2 Docs: Clarify eslint:recommended semver policy (#12429) (Kevin Partington)
    • 97045ae Docs: Fixes object type for rules in "Use a Plugin" (#12409) (Daisy Develops)
    • 24ca088 Docs: Fix typo in v6 migration guide (#12412) (Benjamim Sonntag)
    • b094008 Chore: update version parameter name (#12402) (Toru Nagashima)
    • e5637ba Chore: enable jsdoc/require-description (#12365) (Kai Cataldo)
    • d31f337 Sponsors: Sync README with website (ESLint Jenkins)
    • 7ffb22f Chore: Clean up inline directive parsing (#12375) (Jordan Eldredge)
    • 84467c0 Docs: fix wrong max-depth example (fixes #11991) (#12358) (Gabriel R Sezefredo)
    • 3642342 Docs: Fix minor formatting/grammar errors (#12371) (cherryblossom000)
    • c47fa0d Docs: Fix missing word in sentence (#12361) (Dan Boulet)
    • 8108f49 Chore: enable additional eslint-plugin-jsdoc rules (#12336) (Kai Cataldo)
    • b718d2e Chore: update issue template with --eslint-fix flag (#12352) (James George)
    ... (truncated)
    Commits
    • 879c373 6.6.0
    • c8ba30a Build: changelog update for 6.6.0
    • 39dfe08 Update: false positives in function-call-argument-newline (fixes #12123) (#12...
    • 4d84210 Update: improve report location for no-trailing-spaces (fixes #12315) (#12477)
    • c6a7745 Update: no-trailing-spaces false negatives after comments (fixes #12479) (#12...
    • 0bffe95 Fix: no-misleading-character-class crash on invalid regex (fixes #12169) (#12...
    • c6a9a3b Update: Add enforceForIndexOf option to use-isnan (fixes #12207) (#12379)
    • 364877b Update: measure plugin loading time and output in debug message (#12395)
    • 1744fab Fix: operator-assignment removes and duplicates comments (#12485)
    • 52ca11a Fix: operator-assignment invalid autofix with adjacent tokens (#12483)
    • Additional commits viewable in compare view
    Maintainer changes

    This version was pushed to npm by eslintbot, a new releaser for eslint since your current version.


    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot ignore this [patch|minor|major] version will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • TSERS 2.x roadmap

    TSERS 2.x roadmap

    I'm creating this PR for TSERS 2.x release discussion and planning.

    My proposed changes for 2.x are documented in roadmap.md document in this branch. Please feel free to comment and discuss about the proposed changes in this PR!

    opened by milankinen 6
  • Add router interpreter based on example's router implementation

    Add router interpreter based on example's router implementation

    It would be nice if Router (from examples) become to an indenpendent Github repo (tsers-js/router) and a npm package. Nearly every application need a router (even simple) and it would be easier to import a router interpreter than code your own router or copy from examples. Thank you again for TSERS.

    Feature Driver Help plz?! 
    opened by pmros 2
  • Add SVG support to snabbdom

    Add SVG support to snabbdom

    Continuing discussion from tsers-js/snabbdom#2

    Nice observations here from @laszlokorte:

    I just tried to create my first TSERS component using SVG.

    https://gist.github.com/laszlokorte/2a9ae0accca2805bde3eb28c376fd660#file-tsers-component-js-L11-L13

    The width and height attributes of the svg tag itself get set but the cx, cy and r attributes of the circle tag are not set.

    I think the reason is this check: https://github.com/tsers-js/snabbdom/blob/master/src/index.js#L104-L107 which tries to unify snabbdoms props and attrs options.

    DOM Driver Help plz?! 
    opened by milankinen 6
Releases(0.8.0)
Owner
TSERS
Transform-Signal-Executor framework for Reactive Streams
TSERS
Parse, validate and transform data with confidence in nuxt using zod

nuxt-parse A nuxt focused package to make data validation and parsing easy. This package follows the design philosophy of the article parse, don't val

sidebase 12 Jan 7, 2023
The Intuitive Vue Framework

Build your next Vue.js application with confidence using Nuxt: a framework making web development simple and powerful. Links ?? Documentation: https:/

Nuxt 41.8k Jan 5, 2023
🐉 Material Component Framework for Vue

Supporting Vuetify Vuetify is a MIT licensed project that is developed and maintained full-time by John Leider and Heather Leider; with support from t

vuetify 36.2k Jan 3, 2023
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

vue-next This is the repository for Vue 3.0. Quickstart Via CDN: <script src="https://unpkg.com/vue@next"></script> In-browser playground on Codepen S

vuejs 34.6k Jan 9, 2023
Quasar Framework - Build high-performance VueJS user interfaces in record time

Quasar Framework Build high-performance VueJS user interfaces in record time: responsive Single Page Apps, SSR Apps, PWAs, Browser extensions, Hybrid

Quasar Framework 22.7k Jan 9, 2023
Mobile app development framework and SDK using HTML5 and JavaScript. Create beautiful and performant cross-platform mobile apps. Based on Web Components, and provides bindings for Angular 1, 2, React and Vue.js.

Onsen UI - Cross-Platform Hybrid App and PWA Framework Onsen UI is an open source framework that makes it easy to create native-feeling Progressive We

null 8.7k Jan 4, 2023
Vue Native is a framework to build cross platform native mobile apps using JavaScript

Vue Native Visit our website at vue-native.io or read the official documentation here. Build native mobile apps using Vue Vue Native is a framework to

GeekyAnts 8.4k Jan 6, 2023
⚡️ The Jamstack framework for Vue.js

Gridsome Build super fast, modern websites with Vue.js Gridsome is a Vue-powered static site generator for building CDN-ready websites for any headles

Gridsome 8.4k Dec 30, 2022
New Framework Components for Vue.js 2

Supporting through Patreon Vuesax is an open source MIT project if you want to contribute to keep improving, If you are interested in supporting this

Lusaxweb 5.5k Dec 30, 2022
A hackable slideshow framework built with Vue.js

Eagle.js - A slideshow framework for hackers Slideshow system built on top of the Vue.js Supports animations, themes, interactive widgets (for web dem

null 4.1k Dec 28, 2022
🏝 Opinionated Web Components Starter template to help kick-start development of a cross-framework component library.

Web Component Library Starter Kit "Why create components for a specific framework when it can be written to be understood by all — including browsers?

Open Web Labs 14 Dec 24, 2022
Minimal, zero-configuration and fast solution for static site generation in any front-end framework.

Staticit - Introduction Whether you want to increase performance of your web application or improve SEO. Generally you have 2 options, use SSR (Server

Engineerhub 4 Jun 11, 2022
🏝 Opinionated Web Components Starter template to help kick-start development of a cross-framework component library.

Web Component Library Starter Kit "Why create components for a specific framework when it can be written to be understood by all — including browsers?

Open Web Labs 5 May 1, 2022
AT-UI is a modular front-end UI framework for developing fast and powerful web interfaces based on Vue.js.

AT UI AT-UI is a modular front-end UI framework for developing fast and powerful web interfaces based on Vue.js. 中文 README Features Based on Vue A npm

null 2.3k Jan 4, 2023
The fullstack Angular meta-framework

Analog Analog is a meta-framework for building applications and websites with Angular. Getting Started Use your package manager of choice to create a

null 583 Dec 23, 2022
a babel plugin that can transform generator function to state machine, which is a ported version of typescript generator transform

Babel Plugin Lite Regenerator intro This babel plugin is a ported version of TypeScript generator transform. It can transform async and generator func

Shi Meng 6 Jul 8, 2022
An Azure blob storage executor module for Runnerty

Smart Processes Management Azure blob storage executor for Runnerty: Installation: To be described after packaging. Tested locally using: npm link exe

null 3 Oct 13, 2021
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022