What's new in v0.16

This is a distillation of what’s new in Orbit v0.16, intended as a reference for developers who need to upgrade their apps and libraries from v0.15.

Changelog and release notes

The v0.16 beta cycle represents the first releases with a formal changelog and release notes. We’re committed to continuing this process going forward.

New (and renamed) packages

New packages have been introduced since v0.15:

Build targets

Orbit continues to ship with a matrix of distributions that combine different module types (ESM, CJS, and even AMD) with language levels (ES5 and ES2017).

Because the codebase now extensively uses async / await as well as for ... of, the ES5 builds now must be paired with the regenerator-runtime package to ensure that the regeneratorRuntime global is defined.

The default builds targeted by each package’s main and module are now ES-latest. These builds are as small and performant as possible, and of course can be further processed with Babel if necessary.

Important: There were still some build issues in v0.16.0, which have been resolved in v0.16.1. Please upgrade!

Request hints

Orbit v0.16 introduces the concept of “hints”, which allow request listeners to influence the results that a source returns from that request.

The main reason to use hints is to allow sources to take into account outside information when processing a request. For instance, let’s say that a user queries a memory source and wants records returned in the same order they’re returned from the server. If the server is using a complex sorting algorithm, it may be impossible to recreate that same logic (and full dataset) on the client in the MemorySource.

Read more about using hints in the guide to coordination strategies.

Memory sources can now be “rebased”

A new rebase method has been added to @orbit/memory. The rebase method works similarly to a git rebase. After a memory source has been forked, there will be two sources: a base and a fork. Both may be updated with transforms. When fork.rebase() is called, any commits on the fork will be undone, the commits to the base store since the fork point will be replayed on the fork, and then the commits on the fork will be replayed on top.

Polymorphic relationship support

Relationships in your schemas can now specify multiple possible models as an array in the model field. For example:

  {
models: {
star: {
relationships: {
celestialObjects: { type: 'hasMany', model: ['planet', 'moon'], inverse: 'star' }
}
},
}
}

In this way, a star can have both planets and moons as celestialObjects. Orbit is now one step closer to full JSON:API compliance! ⭐️

JSON:API source customizations

The @orbit/jsonapi source now does its request processing in a new customizable JSONAPIRequestProcessor and JSONAPIURLBuilder classes.

Preliminary: Support JSON:API operations

This release introduces preliminary and partial support for JSON:API Operations in @orbit/jsonapi, which are expected to be introduced to the JSON:API spec soon. Serialization and deserialization support for operations has been added to the JSONAPISerializer class. Further support will be added to the JSONAPISource for processing requests that include operations.

Breaking: New expectations for sources!

While testing the hinting feature and its interactions with different source interfaces, Paul Chavard (@tchak) uncovered some scenarios in which hints could fail to return expected results (see #612). This led to a quest to understand those problems and fix them across source interfaces. Unfortunately, in order to guarantee predictable and consistent behavior, we needed to move some responsibilities to source implementations that were previously handled in Orbit’s private implementations of those interfaces.

Thus, if you’re writing sources, please ensure that you do the following in any of your internal implementation methods that work with transforms (e.g. _update, _sync, etc.):

By moving these responsibilities to source implementations, we allow more flexibility in implementations. For instance, _update can ignore re-applying transforms that may have been sync’d via beforeUpdate listeners, while still returning results that are specified via the hints argument.

In order to update the @orbit/memory source to both support hints and respond to the above requirements, the following changes were needed:

  /////////////////////////////////////////////////////////////////////////////
// Syncable interface implementation
/////////////////////////////////////////////////////////////////////////////

async _sync(transform: Transform): Promise<void> {
- this._applyTransform(transform);
+ if (!this.transformLog.contains(transform.id)) {
+ this._applyTransform(transform); // <- internal implementation that applies the transform to the source's cache
+ await this.transformed([transform]);
+ }
}

/////////////////////////////////////////////////////////////////////////////
// Updatable interface implementation
/////////////////////////////////////////////////////////////////////////////

async _update(transform: Transform, hints?: any): Promise<any> {
- return this._applyTransform(transform);
+ let results: PatchResultData[];
+
+ if (!this.transformLog.contains(transform.id)) {
+ results = this._applyTransform(transform); // <- internal implementation that applies the transform to the source's cache
+ await this.transformed([transform]);
+ }
+
+ if (hints && hints.data) {
+ if (transform.operations.length > 1 && Array.isArray(hints.data)) {
+ return hints.data.map((idOrIds: RecordIdentity | RecordIdentity[]) =>
+ this._retrieveFromCache(idOrIds)
+ );
+ } else {
+ return this._retrieveFromCache(hints.data);
+ }
+ } else if (results) {
+ if (transform.operations.length === 1 && Array.isArray(results)) {
+ return results[0];
+ } else {
+ return results;
+ }
+ }
}

/////////////////////////////////////////////////////////////////////////////
// Queryable interface implementation
/////////////////////////////////////////////////////////////////////////////

async _query(query: Query, hints?: any): Promise<any> {
- return this._cache.query(query);
+ if (hints && hints.data) {
+ return this._retrieveFromCache(hints.data);
+ } else {
+ return this._cache.query(query);
+ }
}

Please review your custom sources to make any necessary changes. If your methods don’t support hints, the diffs should rather small, like in the _sync method above. If you want to support hints, you’ll need to provide alternate code paths for when hints are provided and not, like in the _update and _query methods above.

We’re planning to include a guide to writing your own sources with the v0.16 release to clarify all these responsibilities.

Other changes

Other deprecations / breaking changes

Committers

Many thanks to the committers who made v0.16 possible: