Skip to content

Releases: MatAtBread/AI-UI

Prefer AsyncIterable over AsyncIterator to reduce errors related to consumers that share iterators

15 Dec 17:35

Choose a tag to compare

This mitigates some of the issues created by async protocol errors where a single iterator could end up being doubly referenced. It also goes some way to rendering multi() as obsolete..

Also:

  • Fix case where a removed node has been re-added (but was treated as removed)
  • Apply the same robustness fix from 0.18.7 to filterMap, merge and combine

Full Changelog: https://github.com/MatAtBread/AI-UI/compare/Release-v0.18.7..Release-v0.19.0

Release-v0.18.7: Performance / robustness optimizations and type hygiene

09 Dec 23:51
5904d0e

Choose a tag to compare

This is a maintenance release that fixes some edge cases and irritations:

  • EventObservations are now held in a 3-level map, rather 2-level + array, for performance. In the case where you have thousands of outstanding when iterators, this is a significant saving in CPU cycles.
  • A fix to async attributes to prevent then double-closing the async source if the receiving element is removed from the DOM
  • A re-implementation of Iterators.multi() to make them more robust if a consumer breaks the AsyncIterator protocol by, for example, closing the source iterator more than once (see above)
  • Improvements in the TypeScript definitions to reduce the requirements for explicit casts in AI-UI apps and have better IDE prompting.

v0.18.3: Improved typings. Add optional `initially` to `Iterators.combine()`

27 Aug 14:51
58453b2

Choose a tag to compare

V0.18.3 Add optional initially to Iterators.combine(). Permit promised initial values in filterMap.

What's Changed

  • V0.17.3 by @matAtWork in #47
  • V0.17.4: Improved typings, use element attributes for non-HTML nodes by @matAtWork in #48
  • V0.18.3 Add optional initially to Iterators.combine(). Permit promised initial values in filterMap. by @MatAtBread in #49

Full Changelog: Release-v0.17.4...Release-v0.18.3

V0.17.4: Improved typings, use element attributes for non-HTML nodes

03 Jan 13:54

Choose a tag to compare

This release improves the typescript typings and uses a heuristic to determine whether to set element fields as JS properties (HTML elements) or DOM attributes (others, eg SVG elements).

Note that the improved typings do not permit assignment of iterable properties holding arrays. These still need a type cast since Typescript cannot have different types for setters and getters with computed property names.

Full Changelog: Release-v0.17.3...Release-v0.17.4

v0.17.3. Fix leak & regression

03 Dec 16:31

Choose a tag to compare

Fixes

  • Minor leak in debugging output
  • Regression in the expression this.when('@ready') when no child selectors were ever specified (the async iterator would terminate immediately without yielding an intermediate {})

Full Changelog: Release-v0.17.2...Release-v0.17.3

v0.17.2: Fixes + ErrorTag option

28 Nov 15:13
88da790

Choose a tag to compare

Fixes + ErrorTag option

  • Fix leaks in async attributes
  • Correct implementations of AsyncIterator.throw()
  • Add debug info to deferred & ExtraAsyncs

What's Changed

Full Changelog: Release-v0.17.1...Release-v0.17.2

Release-v0.17.1: Performance optimizations & leak fixes

26 Nov 21:37

Choose a tag to compare

Breaking change

In this release tag.nodes(...) returns in Iterator<Node>, not a Node[]. In most cases, the iterator will substitute just fine, with no modification. The exception is that the iterator can only be spread once. For example (not that this is very useful)

const n = tag.nodes("ABC");
// Works as expected, spreads the nodes in `n` and appends them to the document
document.body.append(...n); 
// Oops! A second attempt to spread `n` returns an empty list - we have already exhausted the `Iterator<Node>`
document.body.append(...n);
/* (as it happens, in this case, this generates the same DOM as using arrays, since inserting the same nodes twice into a DOM moves them, it doesn't clone them) */

If you really want to re-use the nodes, spread them into an array, which will store them, thus: const n = [...tag.nodes("ABC")];

Why has this change taken place?

AI-UI could leak "detached" nodes when it used arrays. Any return from tag.nodes(...) that went unused would leak, which includes some internal races when the tags are dynamic and co-dependent (for example if a child nodes depended on an event generated by an ancestor, which itself depended on the same event or one directly from it). Using an Iterator<Node> avoids a great many of these cases by not actually creating the nodes until they are consumed.

Although it didn't leak, this also address another issue, which is if the returned nodes or their attributes were dynamic (via an async iterator or promise) and NOT inserted into a document, there were cases where the update could fail (with a warning). This might be happen if you made nodes which tried to update themselves, awaited some other async operation and finally inserted them into the DOM. In practice this doesn't happen often as the natural way to do this is to insert the promise of the nodes, not the nodes themselves.

Other optimizations and leak fixes

  • Iterators.merge and Iterators.combine no longer rely on a forever Promise, avoiding cases where the promise chain leaks
  • Rather than waiting for a async iterator to yield before checking if a dynamic node is no longer in the DOM, it is also checked when it is removed, meaning resources are freed quicker (and possibly freed at all in the case of an async iterator that doesn't yield)

Full Changelog: Release-v0.15.5...Release-v0.17.1

Create by ID. Improve typings. Childless `when` selectors

22 Sep 01:53

Choose a tag to compare

Changes

Typing of iterable arrays has been improved

Please see the notes at https://github.com/MatAtBread/AI-UI/blob/v0.15.5/module/src/iterators.ts#L65-L72 about assigning an entire array in one statement.

Create children by id & related tag function

Within the scope of an extended tag, this.ids resolves not only as an object with live connections to contained elements by id (as previously), but also a function that can be used to create children by id, for example:

const MyDiv = div.extended({
  ids:{
   summary: span,
   details: table
  },
  constructed() {
    return [
      h1('Sales report'),
      // Create by ID, in this case a span
      this.ids({ id: 'summary' }, geSummary()),
      // Create by ID, in this case a table
      this.ids({ id: 'details' }, ...),
      div('Created on ', new Date().toString())
    ]
  }
});

This style removes the chance of the ids incorrectly referring to a different tag type.

"Childless" when selectors

When selectors, which fire when an identified child or any of its children generates the specified event, can have a ">" suffix with no grandchild specified to suppress grand-child elements fire. For example:

this.when('#form').consume(...); // Fires when the child id 'form' fires a change, or any of it's children do

this.when('#form>').consume(...); // Fires when the child id 'form' fires a change, bit ignores any fired by children of '#form'

Improvements

  • ValidWhenSelector and CSSIdentifier type improved
  • EventObservation now uses a WeakRef to improve GC performance if an Element is removed and no event fires that would cause it to be removed
  • Improvements in the typing of tags and tag functions
  • Some internal type names have been improved

Full Changelog: Release-v0.15.2...Release-v0.15.5

v0.15.2: Optimize `.ids`, better type checks,

15 Jul 16:35
6db7ff3

Choose a tag to compare

This release focuses on performance, removing legacy code, and ensuring document is correctly referenced.

The guide, examples and tests have been updated to reflect the changes, and the enhancements in v0.14.x for iterable objects, arrays and other fixes,

Breaking changes:

  • The legacy function getElementIDMap() has been removed
    • No longer used internally, and it was a slow function as it ended up enumerating a lot of nodes.
  • The legacy function enableOnRemovedFromDOM() has been removed
    • To enable this deprecated functionality, pass the flag enableOnRemovedFromDOM: true to the tag() options.
  • The legacy method for creating an element in a non-global document (by specifying it in the tag function attributes) has been removed. You can, if necessary, pass an alternative document to the tag() function options.
    • This is rarely necessary. The only real use cases are within a shadow DOM or an IFrame. In the latter case it is better if the frame calls tag() itself.

Optimization

  • The implementation of .ids has been completely re-written to lazily instantiate a proxy that caches elements keyed by their ID, and it is now a "live" implementation like an HTMLCollection. Assigning to .ids or any member of it now throws an exception.
  • Use common forever in Iterators.merge/combine to reduce memory footprint

Better types

  • Correctly define SpecialWhenEvents as having no members
  • Add excess keys test to extended definitions
  • add debugger non-persistent attribute specifier
  • attributes is now typed correctly for read (NamedNodeMap) and write (object). The write type doesn't yet enforce the attribute names/types.

Remove global references to document

The library no longer refers to the global document. You can specify a document in the TagFunctionOptions, eg: const { div } = tag({ document: frameDocument }). The default is to use the global document. All other document references are derived from the elements as they are managed. Removing this dependancy means you can create tag functions that work on a different document.

  • Add optional TagFunctionOptions['docuemnt'] & 'enableOnRemovedFromDOM' (replaces legacy export)
  • Make eventObservations weakly mapped from a document reference
  • reference elt.ownerDocument within elementIsInDOM
  • Move AI-UI style, resolvedNodes mutationTracker & Dom* functions within tag() call to use the specified document

What's Changed

  • v0.15.0 Deprecate legacy calls. Optimize .ids. Remove references to globalThis.document. by @matAtWork in #39
  • v0.15.1 Correctly escape CSS IDs. by @matAtWork in #41

Full Changelog: Release-v0.14.5...Release-v0.15.2

v0.14.5: Fixes & optimizations

02 Jul 14:31

Choose a tag to compare

Optimizations

  • Prefer isAsyncIterator over isAsyncIterable to reuse existing iterators (note: can be fooled by synchronous iterators)
  • Show stack in debug logging
  • export TagLoader type
  • assign initial iterable value in a single Object.assign() in preference to walking down the hierarchy and doing it field by field.

Fixes:

  • Correct test for a queue value in internalDebounceQueueIteratableIterator
  • Don't test path when an initial (top level) iterable value push occurs. This (incorrectly) supressed initial values
  • Assign in the (error) case of an iterable field being re-defined by an extended tag

Full Changelog: Realease-v0.14.4...Release-v0.14.5